diff options
Diffstat (limited to 'modules')
84 files changed, 2643 insertions, 2340 deletions
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 1fd656c9b4..399b102284 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -52,7 +52,7 @@ bool GodotFilterCallback::needBroadphaseCollision(btBroadphaseProxy *proxy0, btB } bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { - if (m_collisionFilterGroup & proxy0->m_collisionFilterMask) { + if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer()); @@ -85,7 +85,7 @@ bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) con return false; } - if (m_collisionFilterGroup & proxy0->m_collisionFilterMask) { + if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer()); if (m_exclude->has(gObj->get_self())) { @@ -117,7 +117,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo } bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { - if (m_collisionFilterGroup & proxy0->m_collisionFilterMask) { + if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer()); if (gObj == m_self_object) { @@ -143,7 +143,7 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox } bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { - if (m_collisionFilterGroup & proxy0->m_collisionFilterMask) { + if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer()); @@ -180,7 +180,7 @@ bool GodotAllContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) co return false; } - if (m_collisionFilterGroup & proxy0->m_collisionFilterMask) { + if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer()); @@ -235,7 +235,7 @@ bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *pr return false; } - if (m_collisionFilterGroup & proxy0->m_collisionFilterMask) { + if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer()); @@ -277,7 +277,7 @@ btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint } bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const { - if (m_collisionFilterGroup & proxy0->m_collisionFilterMask) { + if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer()); diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 8c286a8629..583900e6bc 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -1133,7 +1133,7 @@ public: virtual bool process(const btBroadphaseProxy *proxy) { btCollisionObject *co = static_cast<btCollisionObject *>(proxy->m_clientObject); if (co->getInternalType() <= btCollisionObject::CO_RIGID_BODY) { - if (self_collision_object != proxy->m_clientObject && (collision_layer & proxy->m_collisionFilterMask)) { + if (self_collision_object != proxy->m_clientObject && (proxy->collision_layer & m_collisionFilterMask)) { if (co->getCollisionShape()->isCompound()) { const btCompoundShape *cs = static_cast<btCompoundShape *>(co->getCollisionShape()); diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index fc84c029ec..42f8b9f163 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -135,6 +135,12 @@ void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_i Vector3 ra, rb; Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_id]; + + if (Math::is_nan(d)) { + // The handle is perpendicular to the camera. + return; + } + if (Node3DEditor::get_singleton()->is_snap_enabled()) { d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); } diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml index 01ec46e707..f42ce8c379 100644 --- a/modules/csg/doc_classes/CSGShape3D.xml +++ b/modules/csg/doc_classes/CSGShape3D.xml @@ -10,55 +10,43 @@ </tutorials> <methods> <method name="get_collision_layer_bit" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="bit" type="int"> - </argument> + <return type="bool" /> + <argument index="0" name="bit" type="int" /> <description> Returns an individual bit on the collision mask. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="bit" type="int"> - </argument> + <return type="bool" /> + <argument index="0" name="bit" type="int" /> <description> Returns an individual bit on the collision mask. </description> </method> <method name="get_meshes" qualifiers="const"> - <return type="Array"> - </return> + <return type="Array" /> <description> Returns an [Array] with two elements, the first is the [Transform3D] of this node and the second is the root [Mesh] of this node. Only works when this node is the root shape. </description> </method> <method name="is_root_shape" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <description> Returns [code]true[/code] if this is a root shape and is thus the object that is rendered. </description> </method> <method name="set_collision_layer_bit"> - <return type="void"> - </return> - <argument index="0" name="bit" type="int"> - </argument> - <argument index="1" name="value" type="bool"> - </argument> + <return type="void" /> + <argument index="0" name="bit" type="int" /> + <argument index="1" name="value" type="bool" /> <description> Sets individual bits on the layer mask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> - <return type="void"> - </return> - <argument index="0" name="bit" type="int"> - </argument> - <argument index="1" name="value" type="bool"> - </argument> + <return type="void" /> + <argument index="0" name="bit" type="int" /> + <argument index="1" name="value" type="bool" /> <description> Sets individual bits on the collision mask. Use this if you only need to change one layer's value. </description> diff --git a/modules/enet/config.py b/modules/enet/config.py index 3662b2d94e..9102c74579 100644 --- a/modules/enet/config.py +++ b/modules/enet/config.py @@ -9,6 +9,8 @@ def configure(env): def get_doc_classes(): return [ "ENetMultiplayerPeer", + "ENetConnection", + "ENetPacketPeer", ] diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml new file mode 100644 index 0000000000..c2a85ffdf8 --- /dev/null +++ b/modules/enet/doc_classes/ENetConnection.xml @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ENetConnection" inherits="RefCounted" version="4.0"> + <brief_description> + A wrapper class for an [url=http://enet.bespin.org/group__host.html]ENetHost[/url]. + </brief_description> + <description> + ENet's purpose is to provide a relatively thin, simple and robust network communication layer on top of UDP (User Datagram Protocol). + </description> + <tutorials> + <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link> + </tutorials> + <methods> + <method name="bandwidth_limit"> + <return type="void" /> + <argument index="0" name="in_bandwidth" type="int" default="0" /> + <argument index="1" name="out_bandwidth" type="int" default="0" /> + <description> + Adjusts the bandwidth limits of a host. + </description> + </method> + <method name="broadcast"> + <return type="void" /> + <argument index="0" name="channel" type="int" /> + <argument index="1" name="packet" type="PackedByteArray" /> + <argument index="2" name="flags" type="int" /> + <description> + Queues a [code]packet[/code] to be sent to all peers associated with the host over the specified [code]channel[/code]. See [ENetPacketPeer] [code]FLAG_*[/code] constants for available packet flags. + </description> + </method> + <method name="channel_limit"> + <return type="void" /> + <argument index="0" name="limit" type="int" /> + <description> + Limits the maximum allowed channels of future incoming connections. + </description> + </method> + <method name="compress"> + <return type="void" /> + <argument index="0" name="mode" type="int" enum="ENetConnection.CompressionMode" /> + <description> + Sets the compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. + [b]Note:[/b] Most games' network design involve sending many small packets frequently (smaller than 4 KB each). If in doubt, it is recommended to keep the default compression algorithm as it works best on these small packets. + </description> + </method> + <method name="connect_to_host"> + <return type="ENetPacketPeer" /> + <argument index="0" name="address" type="String" /> + <argument index="1" name="port" type="int" /> + <argument index="2" name="channels" type="int" default="0" /> + <argument index="3" name="data" type="int" default="0" /> + <description> + Initiates a connection to a foreign [code]address[/code] using the specified [code]port[/code] and allocting the requested [code]channels[/code]. Optional [code]data[/code] can be passed during connection in the form of a 32 bit integer. + Note: You must call either [method create_host] or [method create_host_bound] before calling this method. + </description> + </method> + <method name="create_host"> + <return type="int" enum="Error" /> + <argument index="0" name="max_peers" type="int" default="32" /> + <argument index="1" name="max_channels" type="int" default="0" /> + <argument index="2" name="in_bandwidth" type="int" default="0" /> + <argument index="3" name="out_bandwidth" type="int" default="0" /> + <description> + Create an ENetHost that will allow up to [code]max_peers[/code] connected peers, each allocating up to [code]max_channels[/code] channels, optionally limiting bandwith to [code]in_bandwidth[/code] and [code]out_bandwidth[/code]. + </description> + </method> + <method name="create_host_bound"> + <return type="int" enum="Error" /> + <argument index="0" name="bind_address" type="String" /> + <argument index="1" name="bind_port" type="int" /> + <argument index="2" name="max_peers" type="int" default="32" /> + <argument index="3" name="max_channels" type="int" default="0" /> + <argument index="4" name="in_bandwidth" type="int" default="0" /> + <argument index="5" name="out_bandwidth" type="int" default="0" /> + <description> + Create an ENetHost like [method create_host] which is also bound to the given [code]bind_address[/code] and [code]bind_port[/code]. + </description> + </method> + <method name="destroy"> + <return type="void" /> + <description> + Destroys the host and all resources associated with it. + </description> + </method> + <method name="dtls_client_setup"> + <return type="int" enum="Error" /> + <argument index="0" name="certificate" type="X509Certificate" /> + <argument index="1" name="hostname" type="String" /> + <argument index="2" name="verify" type="bool" default="true" /> + <description> + Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet clients. Call this before [method connect_to_host] to have ENet connect using DTLS with [code]certificate[/code] and [code]hostname[/code] verification. Verification can be optionally turned off via the [code]verify[/code] parameter. + </description> + </method> + <method name="dtls_server_setup"> + <return type="int" enum="Error" /> + <argument index="0" name="key" type="CryptoKey" /> + <argument index="1" name="certificate" type="X509Certificate" /> + <description> + Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet servers. Call this right after [method create_host_bound] to have ENet expect peers to connect using DTLS. + </description> + </method> + <method name="flush"> + <return type="void" /> + <description> + Sends any queued packets on the host specified to its designated peers. + </description> + </method> + <method name="get_local_port" qualifiers="const"> + <return type="int" /> + <description> + Returns the local port to which this peer is bound. + </description> + </method> + <method name="get_max_channels" qualifiers="const"> + <return type="int" /> + <description> + Returns the maximum number of channels allowed for connected peers. + </description> + </method> + <method name="get_peers"> + <return type="Array" /> + <description> + Returns the list of peers associated with this host. + Note: This list might include some peers that are not fully connected or are still being disconnected. + </description> + </method> + <method name="pop_statistic"> + <return type="float" /> + <argument index="0" name="statistic" type="int" enum="ENetConnection.HostStatistic" /> + <description> + Returns and resets host statistics. See [enum HostStatistic] for more info. + </description> + </method> + <method name="refuse_new_connections"> + <return type="void" /> + <argument index="0" name="refuse" type="bool" /> + <description> + Configures the DTLS server to automatically drop new connections. + Note: This method is only relevant after calling [method dtls_server_setup]. + </description> + </method> + <method name="service"> + <return type="Array" /> + <argument index="0" name="timeout" type="int" default="0" /> + <description> + Waits for events on the host specified and shuttles packets between the host and its peers. The returned [Array] will have 4 elements. An [enum EventType], the [ENetPacketPeer] which generated the event, the event associated data (if any), the event associated channel (if any). If the generated event is [constant EVENT_RECEIVE], the received packet will be queued to the associated [ENetPacketPeer]. + Call this function regularly to handle connections, disconnections, and to receive new packets. + </description> + </method> + </methods> + <constants> + <constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> + No compression. This uses the most bandwidth, but has the upside of requiring the fewest CPU resources. This option may also be used to make network debugging using tools like Wireshark easier. + </constant> + <constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode"> + ENet's built-in range encoding. Works well on small packets, but is not the most efficient algorithm on packets larger than 4 KB. + </constant> + <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode"> + [url=http://fastlz.org/]FastLZ[/url] compression. This option uses less CPU resources compared to [constant COMPRESS_ZLIB], at the expense of using more bandwidth. + </constant> + <constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode"> + [url=https://www.zlib.net/]Zlib[/url] compression. This option uses less bandwidth compared to [constant COMPRESS_FASTLZ], at the expense of using more CPU resources. + </constant> + <constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode"> + [url=https://facebook.github.io/zstd/]Zstandard[/url] compression. Note that this algorithm is not very efficient on packets smaller than 4 KB. Therefore, it's recommended to use other compression algorithms in most cases. + </constant> + <constant name="EVENT_ERROR" value="-1" enum="EventType"> + An error occurred during [method service]. You will likely need to [method destroy] the host and recreate it. + </constant> + <constant name="EVENT_NONE" value="0" enum="EventType"> + No event occurred within the specified time limit. + </constant> + <constant name="EVENT_CONNECT" value="1" enum="EventType"> + A connection request initiated by enet_host_connect has completed. The array will contain the peer which successfully connected. + </constant> + <constant name="EVENT_DISCONNECT" value="2" enum="EventType"> + A peer has disconnected. This event is generated on a successful completion of a disconnect initiated by [method ENetPacketPeer.peer_disconnect], if a peer has timed out, or if a connection request intialized by [method connect_to_host] has timed out. The array will contain the peer which disconnected. The data field contains user supplied data describing the disconnection, or 0, if none is available. + </constant> + <constant name="EVENT_RECEIVE" value="3" enum="EventType"> + A packet has been received from a peer. The array will contain the peer which sent the packet, the channel number upon which the packet was received, and the received packet. + </constant> + <constant name="HOST_TOTAL_SENT_DATA" value="0" enum="HostStatistic"> + Total data sent. + </constant> + <constant name="HOST_TOTAL_SENT_PACKETS" value="1" enum="HostStatistic"> + Total UDP packets sent. + </constant> + <constant name="HOST_TOTAL_RECEIVED_DATA" value="2" enum="HostStatistic"> + Total data received. + </constant> + <constant name="HOST_TOTAL_RECEIVED_PACKETS" value="3" enum="HostStatistic"> + Total UDP packets received. + </constant> + </constants> +</class> diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index 30dec5987b..3a37b396a4 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ENetMultiplayerPeer" inherits="MultiplayerPeer" version="4.0"> <brief_description> - PacketPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library. + A MultiplayerPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library. </brief_description> <description> - A PacketPeer implementation that should be passed to [member MultiplayerAPI.network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals. - ENet's purpose is to provide a relatively thin, simple and robust network communication layer on top of UDP (User Datagram Protocol). + A MultiplayerPeer implementation that should be passed to [member MultiplayerAPI.network_peer] after being initialized as either a client, server, or mesh. Events can then be handled by connecting to [MultiplayerAPI] signals. See [ENetConnection] for more information on the ENet library wrapper. [b]Note:[/b] ENet only uses UDP, not TCP. When forwarding the server port to make your server accessible on the public Internet, you only need to forward the server port in UDP. You can use the [UPNP] class to try to forward the server port automatically when starting the server. </description> <tutorials> @@ -13,183 +12,77 @@ <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link> </tutorials> <methods> - <method name="close_connection"> - <return type="void"> - </return> - <argument index="0" name="wait_usec" type="int" default="100"> - </argument> + <method name="add_mesh_peer"> + <return type="int" enum="Error" /> + <argument index="0" name="peer_id" type="int" /> + <argument index="1" name="host" type="ENetConnection" /> <description> - Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server. + Add a new remote peer with the given [code]peer_id[/code] connected to the given [code]host[/code]. + Note: The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state. </description> </method> - <method name="create_client"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="address" type="String"> - </argument> - <argument index="1" name="port" type="int"> - </argument> - <argument index="2" name="in_bandwidth" type="int" default="0"> - </argument> - <argument index="3" name="out_bandwidth" type="int" default="0"> - </argument> - <argument index="4" name="local_port" type="int" default="0"> - </argument> - <description> - Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]local_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques. - </description> - </method> - <method name="create_server"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="port" type="int"> - </argument> - <argument index="1" name="max_clients" type="int" default="32"> - </argument> - <argument index="2" name="in_bandwidth" type="int" default="0"> - </argument> - <argument index="3" name="out_bandwidth" type="int" default="0"> - </argument> - <description> - Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4095 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the server could not be created. - </description> - </method> - <method name="disconnect_peer"> - <return type="void"> - </return> - <argument index="0" name="id" type="int"> - </argument> - <argument index="1" name="now" type="bool" default="false"> - </argument> - <description> - Disconnect the given peer. If "now" is set to [code]true[/code], the connection will be closed immediately without flushing queued messages. - </description> - </method> - <method name="get_last_packet_channel" qualifiers="const"> - <return type="int"> - </return> + <method name="close_connection"> + <return type="void" /> + <argument index="0" name="wait_usec" type="int" default="100" /> <description> - Returns the channel of the last packet fetched via [method PacketPeer.get_packet]. + Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server. </description> </method> - <method name="get_local_port" qualifiers="const"> - <return type="int"> - </return> + <method name="create_client"> + <return type="int" enum="Error" /> + <argument index="0" name="address" type="String" /> + <argument index="1" name="port" type="int" /> + <argument index="2" name="channel_count" type="int" default="0" /> + <argument index="3" name="in_bandwidth" type="int" default="0" /> + <argument index="4" name="out_bandwidth" type="int" default="0" /> + <argument index="5" name="local_port" type="int" default="0" /> <description> - Returns the local port to which this peer is bound. + Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]channel_count[/code] parameter can be used to specify the number of ENet channels allocated for the connection. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]local_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques. </description> </method> - <method name="get_packet_channel" qualifiers="const"> - <return type="int"> - </return> + <method name="create_mesh"> + <return type="int" enum="Error" /> + <argument index="0" name="unique_id" type="int" /> <description> - Returns the channel of the next packet that will be retrieved via [method PacketPeer.get_packet]. + Initialize this [MultiplayerPeer] in mesh mode. The provided [code]unique_id[/code] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.network_peer]. In the mesh configuration you will need to set up each new peer manually using [ENetConnection] before calling [method add_mesh_peer]. While this technique is more advanced, it allows for better control over the connection process (e.g. when dealing with NAT punch-through) and for better distribution of the network load (which would otherwise be more taxing on the server). </description> </method> - <method name="get_peer_address" qualifiers="const"> - <return type="String"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <method name="create_server"> + <return type="int" enum="Error" /> + <argument index="0" name="port" type="int" /> + <argument index="1" name="max_clients" type="int" default="32" /> + <argument index="2" name="max_channels" type="int" default="0" /> + <argument index="3" name="in_bandwidth" type="int" default="0" /> + <argument index="4" name="out_bandwidth" type="int" default="0" /> <description> - Returns the IP address of the given peer. + Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4095 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the server could not be created. </description> </method> - <method name="get_peer_port" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <method name="get_peer" qualifiers="const"> + <return type="ENetPacketPeer" /> + <argument index="0" name="id" type="int" /> <description> - Returns the remote port of the given peer. + Return the [ENetPacketPeer] associated to the given [code]id[/code]. </description> </method> <method name="set_bind_ip"> - <return type="void"> - </return> - <argument index="0" name="ip" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="ip" type="String" /> <description> The IP used when creating a server. This is set to the wildcard [code]"*"[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]"192.168.1.1"[/code]. </description> </method> - <method name="set_dtls_certificate"> - <return type="void"> - </return> - <argument index="0" name="certificate" type="X509Certificate"> - </argument> - <description> - Configure the [X509Certificate] to use when [member use_dtls] is [code]true[/code]. For servers, you must also setup the [CryptoKey] via [method set_dtls_key]. - </description> - </method> - <method name="set_dtls_key"> - <return type="void"> - </return> - <argument index="0" name="key" type="CryptoKey"> - </argument> - <description> - Configure the [CryptoKey] to use when [member use_dtls] is [code]true[/code]. Remember to also call [method set_dtls_certificate] to setup your [X509Certificate]. - </description> - </method> - <method name="set_peer_timeout"> - <return type="void"> - </return> - <argument index="0" name="id" type="int"> - </argument> - <argument index="1" name="timeout_limit" type="int"> - </argument> - <argument index="2" name="timeout_min" type="int"> - </argument> - <argument index="3" name="timeout_max" type="int"> - </argument> - <description> - Sets the timeout parameters for a peer. The timeout parameters control how and when a peer will timeout from a failure to acknowledge reliable traffic. Timeout values are expressed in milliseconds. - The [code]timeout_limit[/code] is a factor that, multiplied by a value based on the average round trip time, will determine the timeout limit for a reliable packet. When that limit is reached, the timeout will be doubled, and the peer will be disconnected if that limit has reached [code]timeout_min[/code]. The [code]timeout_max[/code] parameter, on the other hand, defines a fixed timeout for which any packet must be acknowledged or the peer will be dropped. - </description> - </method> </methods> <members> - <member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered" default="false"> - Enforce ordered packets when using [constant MultiplayerPeer.TRANSFER_MODE_UNRELIABLE] (thus behaving similarly to [constant MultiplayerPeer.TRANSFER_MODE_UNRELIABLE_ORDERED]). This is the only way to use ordering with the RPC system. - </member> - <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count" default="3"> - The number of channels to be used by ENet. Channels are used to separate different kinds of data. In reliable or ordered mode, for example, the packet delivery order is ensured on a per-channel basis. This is done to combat latency and reduces ordering restrictions on packets. The delivery status of a packet in one channel won't stall the delivery of other packets in another channel. - </member> - <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="ENetMultiplayerPeer.CompressionMode" default="1"> - The compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. - [b]Note:[/b] Most games' network design involve sending many small packets frequently (smaller than 4 KB each). If in doubt, it is recommended to keep the default compression algorithm as it works best on these small packets. - </member> - <member name="dtls_verify" type="bool" setter="set_dtls_verify_enabled" getter="is_dtls_verify_enabled" default="true"> - Enable or disable certificate verification when [member use_dtls] [code]true[/code]. + <member name="host" type="ENetConnection" setter="" getter="get_host"> + The underlying [ENetConnection] created after [method create_client] and [method create_server]. </member> <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true"> Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server. </member> - <member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel" default="-1"> - Set the default channel to be used to transfer data. By default, this value is [code]-1[/code] which means that ENet will only use 2 channels: one for reliable packets, and one for unreliable packets. The channel [code]0[/code] is reserved and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data. See [member channel_count] for more information about ENet channels. - </member> <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" /> - <member name="use_dtls" type="bool" setter="set_dtls_enabled" getter="is_dtls_enabled" default="false"> - When enabled, the client or server created by this peer, will use [PacketPeerDTLS] instead of raw UDP sockets for communicating with the remote peer. This will make the communication encrypted with DTLS at the cost of higher resource usage and potentially larger packet size. - Note: When creating a DTLS server, make sure you setup the key/certificate pair via [method set_dtls_key] and [method set_dtls_certificate]. For DTLS clients, have a look at the [member dtls_verify] option, and configure the certificate accordingly via [method set_dtls_certificate]. - </member> </members> <constants> - <constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> - No compression. This uses the most bandwidth, but has the upside of requiring the fewest CPU resources. This option may also be used to make network debugging using tools like Wireshark easier. - </constant> - <constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode"> - ENet's built-in range encoding. Works well on small packets, but is not the most efficient algorithm on packets larger than 4 KB. - </constant> - <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode"> - [url=http://fastlz.org/]FastLZ[/url] compression. This option uses less CPU resources compared to [constant COMPRESS_ZLIB], at the expense of using more bandwidth. - </constant> - <constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode"> - [url=https://www.zlib.net/]Zlib[/url] compression. This option uses less bandwidth compared to [constant COMPRESS_FASTLZ], at the expense of using more CPU resources. - </constant> - <constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode"> - [url=https://facebook.github.io/zstd/]Zstandard[/url] compression. Note that this algorithm is not very efficient on packets smaller than 4 KB. Therefore, it's recommended to use other compression algorithms in most cases. - </constant> </constants> </class> diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml new file mode 100644 index 0000000000..8f0693fb01 --- /dev/null +++ b/modules/enet/doc_classes/ENetPacketPeer.xml @@ -0,0 +1,183 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ENetPacketPeer" inherits="PacketPeer" version="4.0"> + <brief_description> + A wrapper class for an [url=http://enet.bespin.org/group__peer.html]ENetPeer[/url]. + </brief_description> + <description> + A PacketPeer implementation representing a peer of an [ENetConnection]. + This class cannot be instantiated directly but can be retrieved during [method ENetConnection.service] or via [method ENetConnection.get_peers]. + </description> + <tutorials> + <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link> + </tutorials> + <methods> + <method name="get_channels" qualifiers="const"> + <return type="int" /> + <description> + Returns the number of channels allocated for communication with peer. + </description> + </method> + <method name="get_state" qualifiers="const"> + <return type="int" enum="ENetPacketPeer.PeerState" /> + <description> + Returns the current peer state. See [enum PeerState]. + </description> + </method> + <method name="get_statistic"> + <return type="float" /> + <argument index="0" name="statistic" type="int" enum="ENetPacketPeer.PeerStatistic" /> + <description> + Returns the requested [code]statistic[/code] for this peer. See [enum PeerStatistic]. + </description> + </method> + <method name="is_active" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the peer is currently active (i.e. the associated [ENetConnection] is still valid). + </description> + </method> + <method name="peer_disconnect"> + <return type="void" /> + <argument index="0" name="data" type="int" default="0" /> + <description> + Request a disconnection from a peer. An [constant ENetConnection.EVENT_DISCONNECT] will be generated during [method ENetConnection.service] once the disconnection is complete. + </description> + </method> + <method name="peer_disconnect_later"> + <return type="void" /> + <argument index="0" name="data" type="int" default="0" /> + <description> + Request a disconnection from a peer, but only after all queued outgoing packets are sent. An [constant ENetConnection.EVENT_DISCONNECT] will be generated during [method ENetConnection.service] once the disconnection is complete. + </description> + </method> + <method name="peer_disconnect_now"> + <return type="void" /> + <argument index="0" name="data" type="int" default="0" /> + <description> + Force an immediate disconnection from a peer. No [constant ENetConnection.EVENT_DISCONNECT] will be generated. The foreign peer is not guaranteed to receive the disconnect notification, and is reset immediately upon return from this function. + </description> + </method> + <method name="ping"> + <return type="void" /> + <description> + Sends a ping request to a peer. ENet automatically pings all connected peers at regular intervals, however, this function may be called to ensure more frequent ping requests. + </description> + </method> + <method name="ping_interval"> + <return type="void" /> + <argument index="0" name="ping_interval" type="int" /> + <description> + Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. + </description> + </method> + <method name="reset"> + <return type="void" /> + <description> + Forcefully disconnects a peer. The foreign host represented by the peer is not notified of the disconnection and will timeout on its connection to the local host. + </description> + </method> + <method name="send"> + <return type="int" enum="Error" /> + <argument index="0" name="channel" type="int" /> + <argument index="1" name="packet" type="PackedByteArray" /> + <argument index="2" name="flags" type="int" /> + <description> + Queues a [code]packet[/code] to be sent over the specified [code]channel[/code]. See [code]FLAG_*[/code] constants for available packet flags. + </description> + </method> + <method name="set_timeout"> + <return type="void" /> + <argument index="0" name="timeout" type="int" /> + <argument index="1" name="timeout_min" type="int" /> + <argument index="2" name="timeout_max" type="int" /> + <description> + Sets the timeout parameters for a peer. The timeout parameters control how and when a peer will timeout from a failure to acknowledge reliable traffic. Timeout values are expressed in milliseconds. + The [code]timeout_limit[/code] is a factor that, multiplied by a value based on the average round trip time, will determine the timeout limit for a reliable packet. When that limit is reached, the timeout will be doubled, and the peer will be disconnected if that limit has reached [code]timeout_min[/code]. The [code]timeout_max[/code] parameter, on the other hand, defines a fixed timeout for which any packet must be acknowledged or the peer will be dropped. + </description> + </method> + <method name="throttle_configure"> + <return type="void" /> + <argument index="0" name="interval" type="int" /> + <argument index="1" name="acceleration" type="int" /> + <argument index="2" name="deceleration" type="int" /> + <description> + Configures throttle parameter for a peer. + Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probably by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]). + When the throttle has a value of [constant PACKET_THROTTLE_SCALE], no unreliable packets are dropped by ENet, and so 100% of all unreliable packets will be sent. + When the throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent. + Intermediate values for the throttle represent intermediate probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits of the local and foreign hosts are taken into account to determine a sensible limit for the throttle probability above which it should not raise even in the best of conditions. + </description> + </method> + </methods> + <constants> + <constant name="STATE_DISCONNECTED" value="0" enum="PeerState"> + </constant> + <constant name="STATE_CONNECTING" value="1" enum="PeerState"> + </constant> + <constant name="STATE_ACKNOWLEDGING_CONNECT" value="2" enum="PeerState"> + </constant> + <constant name="STATE_CONNECTION_PENDING" value="3" enum="PeerState"> + </constant> + <constant name="STATE_CONNECTION_SUCCEEDED" value="4" enum="PeerState"> + </constant> + <constant name="STATE_CONNECTED" value="5" enum="PeerState"> + </constant> + <constant name="STATE_DISCONNECT_LATER" value="6" enum="PeerState"> + </constant> + <constant name="STATE_DISCONNECTING" value="7" enum="PeerState"> + </constant> + <constant name="STATE_ACKNOWLEDGING_DISCONNECT" value="8" enum="PeerState"> + </constant> + <constant name="STATE_ZOMBIE" value="9" enum="PeerState"> + </constant> + <constant name="PEER_PACKET_LOSS" value="0" enum="PeerStatistic"> + Mean packet loss of reliable packets as a ratio with respect to the [constant PACKET_LOSS_SCALE]. + </constant> + <constant name="PEER_PACKET_LOSS_VARIANCE" value="1" enum="PeerStatistic"> + Packet loss variance. + </constant> + <constant name="PEER_PACKET_LOSS_EPOCH" value="2" enum="PeerStatistic"> + </constant> + <constant name="PEER_ROUND_TRIP_TIME" value="3" enum="PeerStatistic"> + Mean packet round trip time for reliable packets. + </constant> + <constant name="PEER_ROUND_TRIP_TIME_VARIANCE" value="4" enum="PeerStatistic"> + Variance of the mean round trip time. + </constant> + <constant name="PEER_LAST_ROUND_TRIP_TIME" value="5" enum="PeerStatistic"> + Last recorded round trip time for a reliable packet. + </constant> + <constant name="PEER_LAST_ROUND_TRIP_TIME_VARIANCE" value="6" enum="PeerStatistic"> + Variance of the last trip time recorded. + </constant> + <constant name="PEER_PACKET_THROTTLE" value="7" enum="PeerStatistic"> + </constant> + <constant name="PEER_PACKET_THROTTLE_LIMIT" value="8" enum="PeerStatistic"> + </constant> + <constant name="PEER_PACKET_THROTTLE_COUNTER" value="9" enum="PeerStatistic"> + </constant> + <constant name="PEER_PACKET_THROTTLE_EPOCH" value="10" enum="PeerStatistic"> + </constant> + <constant name="PEER_PACKET_THROTTLE_ACCELERATION" value="11" enum="PeerStatistic"> + </constant> + <constant name="PEER_PACKET_THROTTLE_DECELERATION" value="12" enum="PeerStatistic"> + </constant> + <constant name="PEER_PACKET_THROTTLE_INTERVAL" value="13" enum="PeerStatistic"> + </constant> + <constant name="PACKET_LOSS_SCALE" value="65536"> + The reference scale for packet loss. See [method get_statistic] and [constant PEER_PACKET_LOSS]. + </constant> + <constant name="PACKET_THROTTLE_SCALE" value="32"> + The reference value for throttle configuration. See [method throttle_configure]. + </constant> + <constant name="FLAG_RELIABLE" value="1"> + Mark the packet to be sent as reliable. + </constant> + <constant name="FLAG_UNSEQUENCED" value="2"> + Mark the packet to be sent unsequenced (unreliable). + </constant> + <constant name="FLAG_UNRELIABLE_FRAGMENT" value="8"> + Mark the packet to be sent unreliable even if the packet is too big and needs fragmentation (increasing the chance of it being dropped). + </constant> + </constants> +</class> diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp new file mode 100644 index 0000000000..e833264d6a --- /dev/null +++ b/modules/enet/enet_connection.cpp @@ -0,0 +1,470 @@ +/*************************************************************************/ +/* enet_connection.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "enet_connection.h" + +#include "enet_packet_peer.h" + +#include "core/io/compression.h" +#include "core/io/ip.h" + +void ENetConnection::broadcast(enet_uint8 p_channel, ENetPacket *p_packet) { + ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active."); + ERR_FAIL_COND_MSG(p_channel >= host->channelLimit, vformat("Unable to send packet on channel %d, max channels: %d", p_channel, (int)host->channelLimit)); + enet_host_broadcast(host, p_channel, p_packet); +} + +Error ENetConnection::create_host_bound(const IPAddress &p_bind_address, int p_port, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) { + ERR_FAIL_COND_V_MSG(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER, "Invalid bind IP."); + ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive)."); + + ENetAddress address; + memset(&address, 0, sizeof(address)); + address.port = p_port; +#ifdef GODOT_ENET + if (p_bind_address.is_wildcard()) { + address.wildcard = 1; + } else { + enet_address_set_ip(&address, p_bind_address.get_ipv6(), 16); + } +#else + if (p_bind_address.is_wildcard()) { + address.host = 0; + } else { + ERR_FAIL_COND_V(!p_bind_address.is_ipv4(), ERR_INVALID_PARAMETER); + address.host = *(uint32_t *)p_bind_address.get_ipv4(); + } +#endif + return _create(&address, p_max_peers, p_max_channels, p_in_bandwidth, p_out_bandwidth); +} + +Error ENetConnection::create_host(int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) { + return _create(nullptr, p_max_peers, p_max_channels, p_in_bandwidth, p_out_bandwidth); +} + +void ENetConnection::destroy() { + ERR_FAIL_COND_MSG(!host, "Host already destroyed"); + for (List<Ref<ENetPacketPeer>>::Element *E = peers.front(); E; E = E->next()) { + E->get()->_on_disconnect(); + } + peers.clear(); + enet_host_destroy(host); + host = nullptr; +} + +Ref<ENetPacketPeer> ENetConnection::connect_to_host(const String &p_address, int p_port, int p_channels, int p_data) { + Ref<ENetPacketPeer> out; + ERR_FAIL_COND_V_MSG(!host, out, "The ENetConnection instance isn't currently active."); + ERR_FAIL_COND_V_MSG(peers.size(), out, "The ENetConnection is already connected to a peer."); + ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, out, "The remote port number must be between 1 and 65535 (inclusive)."); + + IPAddress ip; + if (p_address.is_valid_ip_address()) { + ip = p_address; + } else { +#ifdef GODOT_ENET + ip = IP::get_singleton()->resolve_hostname(p_address); +#else + ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4); +#endif + ERR_FAIL_COND_V_MSG(!ip.is_valid(), out, "Couldn't resolve the server IP address or domain name."); + } + + ENetAddress address; +#ifdef GODOT_ENET + enet_address_set_ip(&address, ip.get_ipv6(), 16); +#else + ERR_FAIL_COND_V_MSG(!ip.is_ipv4(), out, "Connecting to an IPv6 server isn't supported when using vanilla ENet. Recompile Godot with the bundled ENet library."); + address.host = *(uint32_t *)ip.get_ipv4(); +#endif + address.port = p_port; + + // Initiate connection, allocating enough channels + ENetPeer *peer = enet_host_connect(host, &address, p_channels > 0 ? p_channels : ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT, p_data); + + if (peer == nullptr) { + return nullptr; + } + out = Ref<ENetPacketPeer>(memnew(ENetPacketPeer(peer))); + peers.push_back(out); + return out; +} + +ENetConnection::EventType ENetConnection::service(int p_timeout, Event &r_event) { + ERR_FAIL_COND_V_MSG(!host, EVENT_ERROR, "The ENetConnection instance isn't currently active."); + ERR_FAIL_COND_V(r_event.peer.is_valid(), EVENT_ERROR); + + // Drop peers that have already been disconnected. + // NOTE: Forcibly disconnected peers (i.e. peers disconnected via + // enet_peer_disconnect*) do not trigger DISCONNECTED events. + List<Ref<ENetPacketPeer>>::Element *E = peers.front(); + while (E) { + if (!E->get()->is_active()) { + peers.erase(E->get()); + } + E = E->next(); + } + + ENetEvent event; + int ret = enet_host_service(host, &event, p_timeout); + + if (ret < 0) { + return EVENT_ERROR; + } else if (ret == 0) { + return EVENT_NONE; + } + switch (event.type) { + case ENET_EVENT_TYPE_CONNECT: { + if (event.peer->data == nullptr) { + Ref<ENetPacketPeer> pp = memnew(ENetPacketPeer(event.peer)); + peers.push_back(pp); + } + r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data); + r_event.data = event.data; + return EVENT_CONNECT; + } break; + case ENET_EVENT_TYPE_DISCONNECT: { + // A peer disconnected. + if (event.peer->data != nullptr) { + Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data); + pp->_on_disconnect(); + peers.erase(pp); + r_event.peer = pp; + r_event.data = event.data; + return EVENT_DISCONNECT; + } + return EVENT_ERROR; + } break; + case ENET_EVENT_TYPE_RECEIVE: { + // Packet reveived. + if (event.peer->data != nullptr) { + Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data); + r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data); + r_event.channel_id = event.channelID; + r_event.packet = event.packet; + return EVENT_RECEIVE; + } + return EVENT_ERROR; + } break; + case ENET_EVENT_TYPE_NONE: + return EVENT_NONE; + default: + return EVENT_NONE; + } +} + +void ENetConnection::flush() { + ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active."); + enet_host_flush(host); +} + +void ENetConnection::bandwidth_limit(int p_in_bandwidth, int p_out_bandwidth) { + ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active."); + enet_host_bandwidth_limit(host, p_in_bandwidth, p_out_bandwidth); +} + +void ENetConnection::channel_limit(int p_max_channels) { + ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active."); + enet_host_channel_limit(host, p_max_channels); +} + +void ENetConnection::bandwidth_throttle() { + ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active."); + enet_host_bandwidth_throttle(host); +} + +void ENetConnection::compress(CompressionMode p_mode) { + ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active."); + Compressor::setup(host, p_mode); +} + +double ENetConnection::pop_statistic(HostStatistic p_stat) { + ERR_FAIL_COND_V_MSG(!host, 0, "The ENetConnection instance isn't currently active."); + uint32_t *ptr = nullptr; + switch (p_stat) { + case HOST_TOTAL_SENT_DATA: + ptr = &(host->totalSentData); + break; + case HOST_TOTAL_SENT_PACKETS: + ptr = &(host->totalSentPackets); + break; + case HOST_TOTAL_RECEIVED_DATA: + ptr = &(host->totalReceivedData); + break; + case HOST_TOTAL_RECEIVED_PACKETS: + ptr = &(host->totalReceivedPackets); + break; + } + ERR_FAIL_COND_V_MSG(ptr == nullptr, 0, "Invalid statistic: " + itos(p_stat)); + uint32_t ret = *ptr; + *ptr = 0; + return ret; +} + +int ENetConnection::get_max_channels() const { + ERR_FAIL_COND_V_MSG(!host, 0, "The ENetConnection instance isn't currently active."); + return host->channelLimit; +} + +int ENetConnection::get_local_port() const { + ERR_FAIL_COND_V_MSG(!host, 0, "The ENetConnection instance isn't currently active."); + ERR_FAIL_COND_V_MSG(!(host->socket), 0, "The ENetConnection instance isn't currently bound"); + ENetAddress address; + ERR_FAIL_COND_V_MSG(enet_socket_get_address(host->socket, &address), 0, "Unable to get socket address"); + return address.port; +} + +void ENetConnection::get_peers(List<Ref<ENetPacketPeer>> &r_peers) { + for (const Ref<ENetPacketPeer> &I : peers) { + r_peers.push_back(I); + } +} + +Array ENetConnection::_get_peers() { + ERR_FAIL_COND_V_MSG(!host, Array(), "The ENetConnection instance isn't currently active."); + Array out; + for (const Ref<ENetPacketPeer> &I : peers) { + out.push_back(I); + } + return out; +} + +Error ENetConnection::dtls_server_setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert) { +#ifdef GODOT_ENET + ERR_FAIL_COND_V_MSG(!host, ERR_UNCONFIGURED, "The ENetConnection instance isn't currently active."); + return enet_host_dtls_server_setup(host, p_key.ptr(), p_cert.ptr()) ? FAILED : OK; +#else + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "ENet DTLS support not available in this build."); +#endif +} + +void ENetConnection::refuse_new_connections(bool p_refuse) { +#ifdef GODOT_ENET + ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active."); + enet_host_refuse_new_connections(host, p_refuse); +#else + ERR_FAIL_MSG("ENet DTLS support not available in this build."); +#endif +} + +Error ENetConnection::dtls_client_setup(Ref<X509Certificate> p_cert, const String &p_hostname, bool p_verify) { +#ifdef GODOT_ENET + ERR_FAIL_COND_V_MSG(!host, ERR_UNCONFIGURED, "The ENetConnection instance isn't currently active."); + return enet_host_dtls_client_setup(host, p_cert.ptr(), p_verify, p_hostname.utf8().get_data()) ? FAILED : OK; +#else + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "ENet DTLS support not available in this build."); +#endif +} + +Error ENetConnection::_create(ENetAddress *p_address, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) { + ERR_FAIL_COND_V_MSG(host != nullptr, ERR_ALREADY_IN_USE, "The ENetConnection instance is already active."); + ERR_FAIL_COND_V_MSG(p_max_peers < 1 || p_max_peers > 4095, ERR_INVALID_PARAMETER, "The number of clients must be set between 1 and 4095 (inclusive)."); + ERR_FAIL_COND_V_MSG(p_max_channels < 0 || p_max_channels > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT, ERR_INVALID_PARAMETER, "Invalid channel count. Must be between 0 and 255 (0 means maximum, i.e. 255)"); + ERR_FAIL_COND_V_MSG(p_in_bandwidth < 0, ERR_INVALID_PARAMETER, "The incoming bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); + ERR_FAIL_COND_V_MSG(p_out_bandwidth < 0, ERR_INVALID_PARAMETER, "The outgoing bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); + + host = enet_host_create(p_address /* the address to bind the server host to */, + p_max_peers /* allow up to p_max_peers connections */, + p_max_channels /* allow up to p_max_channel to be used */, + p_in_bandwidth /* limit incoming bandwidth if > 0 */, + p_out_bandwidth /* limit outgoing bandwidth if > 0 */); + + ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create an ENet host."); + return OK; +} + +Array ENetConnection::_service(int p_timeout) { + Array out; + Event event; + Ref<ENetPacketPeer> peer; + EventType ret = service(p_timeout, event); + out.push_back(ret); + out.push_back(event.peer); + out.push_back(event.data); + out.push_back(event.channel_id); + if (event.packet && event.peer.is_valid()) { + event.peer->_queue_packet(event.packet); + } + return out; +} + +void ENetConnection::_broadcast(int p_channel, PackedByteArray p_packet, int p_flags) { + ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active."); + ERR_FAIL_COND_MSG(p_channel < 0 || p_channel > (int)host->channelLimit, "Invalid channel"); + ERR_FAIL_COND_MSG(p_flags & ~ENetPacketPeer::FLAG_ALLOWED, "Invalid flags"); + ENetPacket *pkt = enet_packet_create(p_packet.ptr(), p_packet.size(), p_flags); + broadcast(p_channel, pkt); +} + +void ENetConnection::_bind_methods() { + ClassDB::bind_method(D_METHOD("create_host_bound", "bind_address", "bind_port", "max_peers", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetConnection::create_host_bound, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("create_host", "max_peers", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetConnection::create_host, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("destroy"), &ENetConnection::destroy); + ClassDB::bind_method(D_METHOD("connect_to_host", "address", "port", "channels", "data"), &ENetConnection::connect_to_host, DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("service", "timeout"), &ENetConnection::_service, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("flush"), &ENetConnection::flush); + ClassDB::bind_method(D_METHOD("bandwidth_limit", "in_bandwidth", "out_bandwidth"), &ENetConnection::bandwidth_limit, DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("channel_limit", "limit"), &ENetConnection::channel_limit); + ClassDB::bind_method(D_METHOD("broadcast", "channel", "packet", "flags"), &ENetConnection::_broadcast); + ClassDB::bind_method(D_METHOD("compress", "mode"), &ENetConnection::compress); + ClassDB::bind_method(D_METHOD("dtls_server_setup", "key", "certificate"), &ENetConnection::dtls_server_setup); + ClassDB::bind_method(D_METHOD("dtls_client_setup", "certificate", "hostname", "verify"), &ENetConnection::dtls_client_setup, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("refuse_new_connections", "refuse"), &ENetConnection::refuse_new_connections); + ClassDB::bind_method(D_METHOD("pop_statistic", "statistic"), &ENetConnection::pop_statistic); + ClassDB::bind_method(D_METHOD("get_max_channels"), &ENetConnection::get_max_channels); + ClassDB::bind_method(D_METHOD("get_local_port"), &ENetConnection::get_local_port); + ClassDB::bind_method(D_METHOD("get_peers"), &ENetConnection::_get_peers); + + BIND_ENUM_CONSTANT(COMPRESS_NONE); + BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER); + BIND_ENUM_CONSTANT(COMPRESS_FASTLZ); + BIND_ENUM_CONSTANT(COMPRESS_ZLIB); + BIND_ENUM_CONSTANT(COMPRESS_ZSTD); + + BIND_ENUM_CONSTANT(EVENT_ERROR); + BIND_ENUM_CONSTANT(EVENT_NONE); + BIND_ENUM_CONSTANT(EVENT_CONNECT); + BIND_ENUM_CONSTANT(EVENT_DISCONNECT); + BIND_ENUM_CONSTANT(EVENT_RECEIVE); + + BIND_ENUM_CONSTANT(HOST_TOTAL_SENT_DATA); + BIND_ENUM_CONSTANT(HOST_TOTAL_SENT_PACKETS); + BIND_ENUM_CONSTANT(HOST_TOTAL_RECEIVED_DATA); + BIND_ENUM_CONSTANT(HOST_TOTAL_RECEIVED_PACKETS); +} + +ENetConnection::~ENetConnection() { + if (host) { + destroy(); + } +} + +size_t ENetConnection::Compressor::enet_compress(void *context, const ENetBuffer *inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 *outData, size_t outLimit) { + Compressor *compressor = (Compressor *)(context); + + if (size_t(compressor->src_mem.size()) < inLimit) { + compressor->src_mem.resize(inLimit); + } + + int total = inLimit; + int ofs = 0; + while (total) { + for (size_t i = 0; i < inBufferCount; i++) { + int to_copy = MIN(total, int(inBuffers[i].dataLength)); + memcpy(&compressor->src_mem.write[ofs], inBuffers[i].data, to_copy); + ofs += to_copy; + total -= to_copy; + } + } + + Compression::Mode mode; + + switch (compressor->mode) { + case COMPRESS_FASTLZ: { + mode = Compression::MODE_FASTLZ; + } break; + case COMPRESS_ZLIB: { + mode = Compression::MODE_DEFLATE; + } break; + case COMPRESS_ZSTD: { + mode = Compression::MODE_ZSTD; + } break; + default: { + ERR_FAIL_V_MSG(0, vformat("Invalid ENet compression mode: %d", compressor->mode)); + } + } + + int req_size = Compression::get_max_compressed_buffer_size(ofs, mode); + if (compressor->dst_mem.size() < req_size) { + compressor->dst_mem.resize(req_size); + } + int ret = Compression::compress(compressor->dst_mem.ptrw(), compressor->src_mem.ptr(), ofs, mode); + + if (ret < 0) { + return 0; + } + + if (ret > int(outLimit)) { + return 0; // Do not bother + } + + memcpy(outData, compressor->dst_mem.ptr(), ret); + + return ret; +} + +size_t ENetConnection::Compressor::enet_decompress(void *context, const enet_uint8 *inData, size_t inLimit, enet_uint8 *outData, size_t outLimit) { + Compressor *compressor = (Compressor *)(context); + int ret = -1; + switch (compressor->mode) { + case COMPRESS_FASTLZ: { + ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_FASTLZ); + } break; + case COMPRESS_ZLIB: { + ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_DEFLATE); + } break; + case COMPRESS_ZSTD: { + ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_ZSTD); + } break; + default: { + } + } + if (ret < 0) { + return 0; + } else { + return ret; + } +} + +void ENetConnection::Compressor::setup(ENetHost *p_host, CompressionMode p_mode) { + ERR_FAIL_COND(!p_host); + switch (p_mode) { + case COMPRESS_NONE: { + enet_host_compress(p_host, nullptr); + } break; + case COMPRESS_RANGE_CODER: { + enet_host_compress_with_range_coder(p_host); + } break; + case COMPRESS_FASTLZ: + case COMPRESS_ZLIB: + case COMPRESS_ZSTD: { + Compressor *compressor = memnew(Compressor(p_mode)); + enet_host_compress(p_host, &(compressor->enet_compressor)); + } break; + } +} + +ENetConnection::Compressor::Compressor(CompressionMode p_mode) { + mode = p_mode; + enet_compressor.context = this; + enet_compressor.compress = enet_compress; + enet_compressor.decompress = enet_decompress; + enet_compressor.destroy = enet_compressor_destroy; +} diff --git a/modules/enet/enet_connection.h b/modules/enet/enet_connection.h new file mode 100644 index 0000000000..0f7744953e --- /dev/null +++ b/modules/enet/enet_connection.h @@ -0,0 +1,138 @@ +/*************************************************************************/ +/* enet_connection.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 ENET_CONNECTION_H +#define ENET_CONNECTION_H + +#include "core/object/ref_counted.h" + +#include "core/crypto/crypto.h" +#include "enet_packet_peer.h" + +#include <enet/enet.h> + +class ENetConnection : public RefCounted { + GDCLASS(ENetConnection, RefCounted); + +public: + enum CompressionMode { + COMPRESS_NONE = 0, + COMPRESS_RANGE_CODER, + COMPRESS_FASTLZ, + COMPRESS_ZLIB, + COMPRESS_ZSTD, + }; + + enum HostStatistic { + HOST_TOTAL_SENT_DATA, + HOST_TOTAL_SENT_PACKETS, + HOST_TOTAL_RECEIVED_DATA, + HOST_TOTAL_RECEIVED_PACKETS, + }; + + enum EventType { + EVENT_ERROR = -1, + EVENT_NONE = 0, + EVENT_CONNECT, + EVENT_DISCONNECT, + EVENT_RECEIVE, + }; + + struct Event { + Ref<ENetPacketPeer> peer; + enet_uint8 channel_id = 0; + enet_uint32 data = 0; + ENetPacket *packet = nullptr; + }; + +protected: + static void _bind_methods(); + +private: + ENetHost *host = nullptr; + List<Ref<ENetPacketPeer>> peers; + + Error _create(ENetAddress *p_address, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth); + Array _service(int p_timeout = 0); + void _broadcast(int p_channel, PackedByteArray p_packet, int p_flags); + Array _get_peers(); + + class Compressor { + private: + CompressionMode mode = COMPRESS_NONE; + Vector<uint8_t> src_mem; + Vector<uint8_t> dst_mem; + ENetCompressor enet_compressor; + + Compressor(CompressionMode mode); + + static size_t enet_compress(void *context, const ENetBuffer *inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 *outData, size_t outLimit); + static size_t enet_decompress(void *context, const enet_uint8 *inData, size_t inLimit, enet_uint8 *outData, size_t outLimit); + static void enet_compressor_destroy(void *context) { + memdelete((Compressor *)context); + } + + public: + static void setup(ENetHost *p_host, CompressionMode p_mode); + }; + +public: + void broadcast(enet_uint8 p_channel, ENetPacket *p_packet); + Error create_host_bound(const IPAddress &p_bind_address = IPAddress("*"), int p_port = 0, int p_max_peers = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0); + Error create_host(int p_max_peers = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0); + void destroy(); + Ref<ENetPacketPeer> connect_to_host(const String &p_address, int p_port, int p_channels, int p_data = 0); + EventType service(int p_timeout, Event &r_event); + void flush(); + void bandwidth_limit(int p_in_bandwidth = 0, int p_out_bandwidth = 0); + void channel_limit(int p_max_channels); + void bandwidth_throttle(); + void compress(CompressionMode p_mode); + double pop_statistic(HostStatistic p_stat); + int get_max_channels() const; + + // Extras + void get_peers(List<Ref<ENetPacketPeer>> &r_peers); + int get_local_port() const; + + // Godot additions + Error dtls_server_setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert); + Error dtls_client_setup(Ref<X509Certificate> p_cert, const String &p_hostname, bool p_verify = true); + void refuse_new_connections(bool p_refuse); + + ENetConnection() {} + ~ENetConnection(); +}; + +VARIANT_ENUM_CAST(ENetConnection::CompressionMode); +VARIANT_ENUM_CAST(ENetConnection::EventType); +VARIANT_ENUM_CAST(ENetConnection::HostStatistic); + +#endif // ENET_CONNECTION_H diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index b4ca93f4d9..28b6bb035a 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -46,458 +46,375 @@ void ENetMultiplayerPeer::set_target_peer(int p_peer) { } int ENetMultiplayerPeer::get_packet_peer() const { - ERR_FAIL_COND_V_MSG(!active, 1, "The multiplayer instance isn't currently active."); + ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active."); ERR_FAIL_COND_V(incoming_packets.size() == 0, 1); return incoming_packets.front()->get().from; } -int ENetMultiplayerPeer::get_packet_channel() const { - ERR_FAIL_COND_V_MSG(!active, -1, "The multiplayer instance isn't currently active."); - ERR_FAIL_COND_V(incoming_packets.size() == 0, -1); - - return incoming_packets.front()->get().channel; -} - -int ENetMultiplayerPeer::get_last_packet_channel() const { - ERR_FAIL_COND_V_MSG(!active, -1, "The multiplayer instance isn't currently active."); - ERR_FAIL_COND_V(!current_packet.packet, -1); +Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) { + ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); + Ref<ENetConnection> host; + host.instantiate(); + Error err = host->create_host_bound(bind_ip, p_port, p_max_clients, 0, p_max_channels > 0 ? p_max_channels + SYSCH_MAX : 0, p_out_bandwidth); + if (err != OK) { + return err; + } - return current_packet.channel; + active_mode = MODE_SERVER; + refuse_connections = false; + unique_id = 1; + connection_status = CONNECTION_CONNECTED; + hosts[0] = host; + return OK; } -Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth) { - ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); - ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive)."); - ERR_FAIL_COND_V_MSG(p_max_clients < 1 || p_max_clients > 4095, ERR_INVALID_PARAMETER, "The number of clients must be set between 1 and 4095 (inclusive)."); - ERR_FAIL_COND_V_MSG(p_in_bandwidth < 0, ERR_INVALID_PARAMETER, "The incoming bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); - ERR_FAIL_COND_V_MSG(p_out_bandwidth < 0, ERR_INVALID_PARAMETER, "The outgoing bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); - ERR_FAIL_COND_V(dtls_enabled && (dtls_key.is_null() || dtls_cert.is_null()), ERR_INVALID_PARAMETER); - - ENetAddress address; - memset(&address, 0, sizeof(address)); - -#ifdef GODOT_ENET - if (bind_ip.is_wildcard()) { - address.wildcard = 1; +Error ENetMultiplayerPeer::create_client(const String &p_address, int p_port, int p_channel_count, int p_in_bandwidth, int p_out_bandwidth, int p_local_port) { + ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); + Ref<ENetConnection> host; + host.instantiate(); + Error err; + if (p_local_port) { + err = host->create_host_bound(bind_ip, p_local_port, 1, 0, p_in_bandwidth, p_out_bandwidth); } else { - enet_address_set_ip(&address, bind_ip.get_ipv6(), 16); + err = host->create_host(1, 0, p_in_bandwidth, p_out_bandwidth); } -#else - if (bind_ip.is_wildcard()) { - address.host = 0; - } else { - ERR_FAIL_COND_V(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER); - address.host = *(uint32_t *)bind_ip.get_ipv4(); + if (err != OK) { + return err; } -#endif - address.port = p_port; - host = enet_host_create(&address /* the address to bind the server host to */, - p_max_clients /* allow up to 32 clients and/or outgoing connections */, - channel_count /* allow up to channel_count to be used */, - p_in_bandwidth /* limit incoming bandwidth if > 0 */, - p_out_bandwidth /* limit outgoing bandwidth if > 0 */); + unique_id = generate_unique_id(); - ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create an ENet multiplayer server."); -#ifdef GODOT_ENET - if (dtls_enabled) { - enet_host_dtls_server_setup(host, dtls_key.ptr(), dtls_cert.ptr()); + Ref<ENetPacketPeer> peer = host->connect_to_host(p_address, p_port, p_channel_count > 0 ? p_channel_count + SYSCH_MAX : 0, unique_id); + if (peer.is_null()) { + host->destroy(); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Couldn't connect to the ENet multiplayer server."); } - enet_host_refuse_new_connections(host, refuse_connections); -#endif - _setup_compressor(); - active = true; - server = true; + // Need to wait for CONNECT event. + connection_status = CONNECTION_CONNECTING; + active_mode = MODE_CLIENT; refuse_connections = false; - unique_id = 1; + peers[1] = peer; + hosts[0] = host; + + return OK; +} + +Error ENetMultiplayerPeer::create_mesh(int p_id) { + ERR_FAIL_COND_V_MSG(p_id <= 0, ERR_INVALID_PARAMETER, "The unique ID must be greater then 0"); + ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); + active_mode = MODE_MESH; + refuse_connections = false; + unique_id = p_id; connection_status = CONNECTION_CONNECTED; return OK; } -Error ENetMultiplayerPeer::create_client(const String &p_address, int p_port, int p_in_bandwidth, int p_out_bandwidth, int p_local_port) { - ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); - ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive)."); - ERR_FAIL_COND_V_MSG(p_local_port < 0 || p_local_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive)."); - ERR_FAIL_COND_V_MSG(p_in_bandwidth < 0, ERR_INVALID_PARAMETER, "The incoming bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); - ERR_FAIL_COND_V_MSG(p_out_bandwidth < 0, ERR_INVALID_PARAMETER, "The outgoing bandwidth limit must be greater than or equal to 0 (0 disables the limit)."); - ENetAddress c_client; +Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) { + ERR_FAIL_COND_V(p_host.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V_MSG(active_mode != MODE_MESH, ERR_UNCONFIGURED, "The multiplayer instance is not configured as a mesh. Call 'create_mesh' first."); + List<Ref<ENetPacketPeer>> host_peers; + p_host->get_peers(host_peers); + ERR_FAIL_COND_V_MSG(host_peers.size() != 1 || host_peers[0]->get_state() != ENetPacketPeer::STATE_CONNECTED, ERR_INVALID_PARAMETER, "The provided host must have excatly one peer in the connected state."); + hosts[p_id] = p_host; + peers[p_id] = host_peers[0]; + emit_signal(SNAME("peer_connected"), p_id); + return OK; +} -#ifdef GODOT_ENET - if (bind_ip.is_wildcard()) { - c_client.wildcard = 1; - } else { - enet_address_set_ip(&c_client, bind_ip.get_ipv6(), 16); - } -#else - if (bind_ip.is_wildcard()) { - c_client.host = 0; - } else { - ERR_FAIL_COND_V_MSG(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER, "Wildcard IP addresses are only permitted in IPv4, not IPv6."); - c_client.host = *(uint32_t *)bind_ip.get_ipv4(); +bool ENetMultiplayerPeer::_poll_server() { + for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (!(E.value->is_active())) { + emit_signal(SNAME("peer_disconnected"), E.value->get_meta(SNAME("_net_id"))); + peers.erase(E.key); + } } -#endif - - c_client.port = p_local_port; - - host = enet_host_create(&c_client /* create a client host */, - 1 /* only allow 1 outgoing connection */, - channel_count /* allow up to channel_count to be used */, - p_in_bandwidth /* limit incoming bandwidth if > 0 */, - p_out_bandwidth /* limit outgoing bandwidth if > 0 */); - - ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create the ENet client host."); -#ifdef GODOT_ENET - if (dtls_enabled) { - enet_host_dtls_client_setup(host, dtls_cert.ptr(), dtls_verify, p_address.utf8().get_data()); + ENetConnection::Event event; + ENetConnection::EventType ret = hosts[0]->service(0, event); + if (ret == ENetConnection::EVENT_ERROR) { + return true; } - enet_host_refuse_new_connections(host, refuse_connections); -#endif + switch (ret) { + case ENetConnection::EVENT_CONNECT: { + if (refuse_connections) { + event.peer->reset(); + return false; + } + // Client joined with invalid ID, probably trying to exploit us. + if (event.data < 2 || peers.has((int)event.data)) { + event.peer->reset(); + return false; + } + int id = event.data; + event.peer->set_meta(SNAME("_net_id"), id); + peers[id] = event.peer; - _setup_compressor(); + emit_signal(SNAME("peer_connected"), id); + if (server_relay) { + _notify_peers(id, true); + } + return false; + } + case ENetConnection::EVENT_DISCONNECT: { + int id = event.peer->get_meta(SNAME("_net_id")); + if (!peers.has(id)) { + // Never fully connected. + return false; + } - IPAddress ip; - if (p_address.is_valid_ip_address()) { - ip = p_address; - } else { -#ifdef GODOT_ENET - ip = IP::get_singleton()->resolve_hostname(p_address); -#else - ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4); -#endif + emit_signal(SNAME("peer_disconnected"), id); + peers.erase(id); + if (!server_relay) { + _notify_peers(id, false); + } + return false; + } + case ENetConnection::EVENT_RECEIVE: { + if (event.channel_id == SYSCH_CONFIG) { + _destroy_unused(event.packet); + ERR_FAIL_V_MSG(false, "Only server can send config messages"); + } else { + if (event.packet->dataLength < 8) { + _destroy_unused(event.packet); + ERR_FAIL_V_MSG(false, "Invalid packet size"); + } - ERR_FAIL_COND_V_MSG(!ip.is_valid(), ERR_CANT_RESOLVE, "Couldn't resolve the server IP address or domain name."); - } + uint32_t source = decode_uint32(&event.packet->data[0]); + int target = decode_uint32(&event.packet->data[4]); - ENetAddress address; -#ifdef GODOT_ENET - enet_address_set_ip(&address, ip.get_ipv6(), 16); -#else - ERR_FAIL_COND_V_MSG(!ip.is_ipv4(), ERR_INVALID_PARAMETER, "Connecting to an IPv6 server isn't supported when using vanilla ENet. Recompile Godot with the bundled ENet library."); - address.host = *(uint32_t *)ip.get_ipv4(); -#endif - address.port = p_port; + uint32_t id = event.peer->get_meta(SNAME("_net_id")); + // Someone is cheating and trying to fake the source! + if (source != id) { + _destroy_unused(event.packet); + ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!"); + } - unique_id = _gen_unique_id(); + Packet packet; + packet.packet = event.packet; + packet.channel = event.channel_id; + packet.from = id; - // Initiate connection, allocating enough channels - ENetPeer *peer = enet_host_connect(host, &address, channel_count, unique_id); + // Even if relaying is disabled, these targets are valid as incoming packets. + if (target == 1 || target == 0 || target < -1) { + packet.packet->referenceCount++; + incoming_packets.push_back(packet); + } - if (peer == nullptr) { - enet_host_destroy(host); - ERR_FAIL_COND_V_MSG(!peer, ERR_CANT_CREATE, "Couldn't connect to the ENet multiplayer server."); + if (server_relay && target != 1) { + packet.packet->referenceCount++; + _relay(source, target, event.channel_id, event.packet); + packet.packet->referenceCount--; + _destroy_unused(event.packet); + } + // Destroy packet later + } + return false; + } + default: + return true; } - - // Technically safe to ignore the peer or anything else. - - connection_status = CONNECTION_CONNECTING; - active = true; - server = false; - refuse_connections = false; - - return OK; } -void ENetMultiplayerPeer::poll() { - ERR_FAIL_COND_MSG(!active, "The multiplayer instance isn't currently active."); - - _pop_current_packet(); - - ENetEvent event; - /* Keep servicing until there are no available events left in queue. */ - while (true) { - if (!host || !active) { // Might have been disconnected while emitting a notification - return; +bool ENetMultiplayerPeer::_poll_client() { + if (peers.has(1) && !peers[1]->is_active()) { + if (connection_status == CONNECTION_CONNECTED) { + // Client just disconnected from server. + emit_signal(SNAME("server_disconnected")); + } else { + emit_signal(SNAME("connection_failed")); } - - int ret = enet_host_service(host, &event, 0); - - if (ret < 0) { - // Error, do something? - break; - } else if (ret == 0) { - break; + close_connection(); + return true; + } + ENetConnection::Event event; + ENetConnection::EventType ret = hosts[0]->service(0, event); + if (ret == ENetConnection::EVENT_ERROR) { + return true; + } + switch (ret) { + case ENetConnection::EVENT_CONNECT: { + emit_signal(SNAME("peer_connected"), 1); + connection_status = CONNECTION_CONNECTED; + emit_signal(SNAME("connection_succeeded")); + return false; + } + case ENetConnection::EVENT_DISCONNECT: { + if (connection_status == CONNECTION_CONNECTED) { + // Client just disconnected from server. + emit_signal(SNAME("server_disconnected")); + } else { + emit_signal(SNAME("connection_failed")); + } + close_connection(); + return true; } + case ENetConnection::EVENT_RECEIVE: { + if (event.channel_id == SYSCH_CONFIG) { + // Config message + if (event.packet->dataLength != 8) { + _destroy_unused(event.packet); + ERR_FAIL_V(false); + } - switch (event.type) { - case ENET_EVENT_TYPE_CONNECT: { - // Store any relevant client information here. + int msg = decode_uint32(&event.packet->data[0]); + int id = decode_uint32(&event.packet->data[4]); - if (server && refuse_connections) { - enet_peer_reset(event.peer); - break; - } + switch (msg) { + case SYSMSG_ADD_PEER: { + peers[id] = Ref<ENetPacketPeer>(); + emit_signal(SNAME("peer_connected"), id); - // A client joined with an invalid ID (negative values, 0, and 1 are reserved). - // Probably trying to exploit us. - if (server && ((int)event.data < 2 || peer_map.has((int)event.data))) { - enet_peer_reset(event.peer); - ERR_CONTINUE(true); + } break; + case SYSMSG_REMOVE_PEER: { + peers.erase(id); + emit_signal(SNAME("peer_disconnected"), id); + } break; + } + _destroy_unused(event.packet); + } else { + if (event.packet->dataLength < 8) { + _destroy_unused(event.packet); + ERR_FAIL_V_MSG(false, "Invalid packet size"); } - int *new_id = memnew(int); - *new_id = event.data; + uint32_t source = decode_uint32(&event.packet->data[0]); + Packet packet; + packet.packet = event.packet; + packet.from = source; + packet.channel = event.channel_id; - if (*new_id == 0) { // Data zero is sent by server (ENet won't let you configure this). Server is always 1. - *new_id = 1; - } + packet.packet->referenceCount++; + incoming_packets.push_back(packet); + // Destroy packet later + } + return false; + } + default: + return true; + } +} - event.peer->data = new_id; - - peer_map[*new_id] = event.peer; - - connection_status = CONNECTION_CONNECTED; // If connecting, this means it connected to something! - - emit_signal(SNAME("peer_connected"), *new_id); - - if (server) { - // Do not notify other peers when server_relay is disabled. - if (!server_relay) { - break; - } - - // Someone connected, notify all the peers available - for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (E->key() == *new_id) { - continue; - } - // Send existing peers to new peer - ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); - encode_uint32(E->key(), &packet->data[4]); - enet_peer_send(event.peer, SYSCH_CONFIG, packet); - // Send the new peer to existing peers - packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); - encode_uint32(*new_id, &packet->data[4]); - enet_peer_send(E->get(), SYSCH_CONFIG, packet); - } - } else { - emit_signal(SNAME("connection_succeeded")); +bool ENetMultiplayerPeer::_poll_mesh() { + for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (!(E.value->is_active())) { + emit_signal(SNAME("peer_disconnected"), E.key); + peers.erase(E.key); + if (hosts.has(E.key)) { + hosts.erase(E.key); + } + } + } + bool should_stop = true; + for (KeyValue<int, Ref<ENetConnection>> &E : hosts) { + ENetConnection::Event event; + ENetConnection::EventType ret = E.value->service(0, event); + if (ret == ENetConnection::EVENT_ERROR) { + if (peers.has(E.key)) { + emit_signal(SNAME("peer_disconnected"), E.key); + peers.erase(E.key); + } + hosts.erase(E.key); + continue; + } + switch (ret) { + case ENetConnection::EVENT_CONNECT: + should_stop = false; + event.peer->reset(); + break; + case ENetConnection::EVENT_DISCONNECT: + should_stop = false; + if (peers.has(E.key)) { + emit_signal(SNAME("peer_disconnected"), E.key); + peers.erase(E.key); + } + hosts.erase(E.key); + break; + case ENetConnection::EVENT_RECEIVE: { + should_stop = false; + if (event.packet->dataLength < 8) { + _destroy_unused(event.packet); + ERR_CONTINUE_MSG(true, "Invalid packet size"); } + Packet packet; + packet.packet = event.packet; + packet.from = E.key; + packet.channel = event.channel_id; + + packet.packet->referenceCount++; + incoming_packets.push_back(packet); } break; - case ENET_EVENT_TYPE_DISCONNECT: { - // Reset the peer's client information. + default: + break; // Nothing to do + } + } + return should_stop; +} - int *id = (int *)event.peer->data; +void ENetMultiplayerPeer::poll() { + ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active."); - if (!id) { - if (!server) { - emit_signal(SNAME("connection_failed")); - } - // Never fully connected. - break; - } + _pop_current_packet(); - if (!server) { - // Client just disconnected from server. - emit_signal(SNAME("server_disconnected")); - close_connection(); + while (true) { + switch (active_mode) { + case MODE_CLIENT: + if (_poll_client()) { return; - } else if (server_relay) { - // Server just received a client disconnect and is in relay mode, notify everyone else. - for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (E->key() == *id) { - continue; - } - - ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); - encode_uint32(*id, &packet->data[4]); - enet_peer_send(E->get(), SYSCH_CONFIG, packet); - } } - - emit_signal(SNAME("peer_disconnected"), *id); - peer_map.erase(*id); - memdelete(id); - } break; - case ENET_EVENT_TYPE_RECEIVE: { - if (event.channelID == SYSCH_CONFIG) { - // Some config message - ERR_CONTINUE(event.packet->dataLength < 8); - - // Only server can send config messages - ERR_CONTINUE(server); - - int msg = decode_uint32(&event.packet->data[0]); - int id = decode_uint32(&event.packet->data[4]); - - switch (msg) { - case SYSMSG_ADD_PEER: { - peer_map[id] = nullptr; - emit_signal(SNAME("peer_connected"), id); - - } break; - case SYSMSG_REMOVE_PEER: { - peer_map.erase(id); - emit_signal(SNAME("peer_disconnected"), id); - } break; - } - - enet_packet_destroy(event.packet); - } else if (event.channelID < channel_count) { - Packet packet; - packet.packet = event.packet; - - uint32_t *id = (uint32_t *)event.peer->data; - - ERR_CONTINUE(event.packet->dataLength < 8); - - uint32_t source = decode_uint32(&event.packet->data[0]); - int target = decode_uint32(&event.packet->data[4]); - - packet.from = source; - packet.channel = event.channelID; - - if (server) { - // Someone is cheating and trying to fake the source! - ERR_CONTINUE(source != *id); - - packet.from = *id; - - if (target == 1) { - // To myself and only myself - incoming_packets.push_back(packet); - } else if (!server_relay) { - // When relaying is disabled, other destinations will only be processed by the server. - if (target == 0 || target < -1) { - incoming_packets.push_back(packet); - } - continue; - } else if (target == 0) { - // Re-send to everyone but sender :| - - incoming_packets.push_back(packet); - // And make copies for sending - for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (uint32_t(E->key()) == source) { // Do not resend to self - continue; - } - - ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags); - - enet_peer_send(E->get(), event.channelID, packet2); - } - - } else if (target < 0) { - // To all but one - - // And make copies for sending - for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (uint32_t(E->key()) == source || E->key() == -target) { // Do not resend to self, also do not send to excluded - continue; - } - - ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags); - - enet_peer_send(E->get(), event.channelID, packet2); - } - - if (-target != 1) { - // Server is not excluded - incoming_packets.push_back(packet); - } else { - // Server is excluded, erase packet - enet_packet_destroy(packet.packet); - } - - } else { - // To someone else, specifically - ERR_CONTINUE(!peer_map.has(target)); - enet_peer_send(peer_map[target], event.channelID, packet.packet); - } - } else { - incoming_packets.push_back(packet); - } - - // Destroy packet later - } else { - ERR_CONTINUE(true); + break; + case MODE_SERVER: + if (_poll_server()) { + return; } - - } break; - case ENET_EVENT_TYPE_NONE: { - // Do nothing - } break; + break; + case MODE_MESH: + if (_poll_mesh()) { + return; + } + break; + default: + return; } } } bool ENetMultiplayerPeer::is_server() const { - ERR_FAIL_COND_V_MSG(!active, false, "The multiplayer instance isn't currently active."); - - return server; + return active_mode == MODE_SERVER; } void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) { - ERR_FAIL_COND_MSG(!active, "The multiplayer instance isn't currently active."); + ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active."); _pop_current_packet(); bool peers_disconnected = false; - for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (E->get()) { - enet_peer_disconnect_now(E->get(), unique_id); - int *id = (int *)(E->get()->data); - memdelete(id); + for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (E.value.is_valid() && E.value->get_state() == ENetPacketPeer::STATE_CONNECTED) { + E.value->peer_disconnect_now(unique_id); peers_disconnected = true; } } if (peers_disconnected) { - enet_host_flush(host); + for (KeyValue<int, Ref<ENetConnection>> &E : hosts) { + E.value->flush(); + } if (wait_usec > 0) { OS::get_singleton()->delay_usec(wait_usec); // Wait for disconnection packets to send } } - enet_host_destroy(host); - active = false; + active_mode = MODE_NONE; incoming_packets.clear(); - peer_map.clear(); - unique_id = 1; // Server is 1 + peers.clear(); + hosts.clear(); + unique_id = 0; connection_status = CONNECTION_DISCONNECTED; } -void ENetMultiplayerPeer::disconnect_peer(int p_peer, bool now) { - ERR_FAIL_COND_MSG(!active, "The multiplayer instance isn't currently active."); - ERR_FAIL_COND_MSG(!is_server(), "Can't disconnect a peer when not acting as a server."); - ERR_FAIL_COND_MSG(!peer_map.has(p_peer), vformat("Peer ID %d not found in the list of peers.", p_peer)); - - if (now) { - int *id = (int *)peer_map[p_peer]->data; - enet_peer_disconnect_now(peer_map[p_peer], 0); - - // enet_peer_disconnect_now doesn't generate ENET_EVENT_TYPE_DISCONNECT, - // notify everyone else, send disconnect signal & remove from peer_map like in poll() - if (server_relay) { - for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) { - if (E->key() == p_peer) { - continue; - } - - ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); - encode_uint32(p_peer, &packet->data[4]); - enet_peer_send(E->get(), SYSCH_CONFIG, packet); - } - } - - if (id) { - memdelete(id); - } - - emit_signal(SNAME("peer_disconnected"), p_peer); - peer_map.erase(p_peer); - } else { - enet_peer_disconnect_later(peer_map[p_peer], 0); - } -} - int ENetMultiplayerPeer::get_available_packet_count() const { return incoming_packets.size(); } @@ -517,19 +434,17 @@ Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_si } Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V_MSG(!active, ERR_UNCONFIGURED, "The multiplayer instance isn't currently active."); + ERR_FAIL_COND_V_MSG(!_is_active(), ERR_UNCONFIGURED, "The multiplayer instance isn't currently active."); ERR_FAIL_COND_V_MSG(connection_status != CONNECTION_CONNECTED, ERR_UNCONFIGURED, "The multiplayer instance isn't currently connected to any server or client."); + ERR_FAIL_COND_V_MSG(target_peer != 0 && !peers.has(ABS(target_peer)), ERR_INVALID_PARAMETER, vformat("Invalid target peer: %d", target_peer)); + ERR_FAIL_COND_V(active_mode == MODE_CLIENT && !peers.has(1), ERR_BUG); int packet_flags = 0; int channel = SYSCH_RELIABLE; switch (transfer_mode) { case TRANSFER_MODE_UNRELIABLE: { - if (always_ordered) { - packet_flags = 0; - } else { - packet_flags = ENET_PACKET_FLAG_UNSEQUENCED; - } + packet_flags = ENET_PACKET_FLAG_UNSEQUENCED; channel = SYSCH_UNRELIABLE; } break; case TRANSFER_MODE_UNRELIABLE_ORDERED: { @@ -542,52 +457,55 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size } break; } - if (transfer_channel > SYSCH_CONFIG) { - channel = transfer_channel; - } - - Map<int, ENetPeer *>::Element *E = nullptr; - - if (target_peer != 0) { - E = peer_map.find(ABS(target_peer)); - ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, vformat("Invalid target peer: %d", target_peer)); - } - ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags); encode_uint32(unique_id, &packet->data[0]); // Source ID encode_uint32(target_peer, &packet->data[4]); // Dest ID memcpy(&packet->data[8], p_buffer, p_buffer_size); - if (server) { + if (is_server()) { if (target_peer == 0) { - enet_host_broadcast(host, channel, packet); - } else if (target_peer < 0) { - // Send to all but one - // and make copies for sending + hosts[0]->broadcast(channel, packet); + } else if (target_peer < 0) { + // Send to all but one and make copies for sending. int exclude = -target_peer; - - for (Map<int, ENetPeer *>::Element *F = peer_map.front(); F; F = F->next()) { - if (F->key() == exclude) { // Exclude packet + for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (E.key == exclude) { continue; } + E.value->send(channel, packet); + } + _destroy_unused(packet); + } else { + peers[target_peer]->send(channel, packet); + } + ERR_FAIL_COND_V(!hosts.has(0), ERR_BUG); + hosts[0]->flush(); - ENetPacket *packet2 = enet_packet_create(packet->data, packet->dataLength, packet_flags); + } else if (active_mode == MODE_CLIENT) { + peers[1]->send(channel, packet); // Send to server for broadcast. + ERR_FAIL_COND_V(!hosts.has(0), ERR_BUG); + hosts[0]->flush(); - enet_peer_send(F->get(), channel, packet2); + } else { + if (target_peer <= 0) { + int exclude = ABS(target_peer); + for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (E.key == exclude) { + continue; + } + E.value->send(channel, packet); + ERR_CONTINUE(!hosts.has(E.key)); + hosts[E.key]->flush(); } - - enet_packet_destroy(packet); // Original packet no longer needed + _destroy_unused(packet); } else { - enet_peer_send(E->get(), channel, packet); + peers[target_peer]->send(channel, packet); + ERR_FAIL_COND_V(!hosts.has(target_peer), ERR_BUG); + hosts[target_peer]->flush(); } - } else { - ERR_FAIL_COND_V(!peer_map.has(1), ERR_BUG); - enet_peer_send(peer_map[1], channel, packet); // Send to server for broadcast } - enet_host_flush(host); - return OK; } @@ -597,7 +515,8 @@ int ENetMultiplayerPeer::get_max_packet_size() const { void ENetMultiplayerPeer::_pop_current_packet() { if (current_packet.packet) { - enet_packet_destroy(current_packet.packet); + current_packet.packet->referenceCount--; + _destroy_unused(current_packet.packet); current_packet.packet = nullptr; current_packet.from = 0; current_packet.channel = -1; @@ -608,37 +527,18 @@ MultiplayerPeer::ConnectionStatus ENetMultiplayerPeer::get_connection_status() c return connection_status; } -uint32_t ENetMultiplayerPeer::_gen_unique_id() const { - uint32_t hash = 0; - - while (hash == 0 || hash == 1) { - hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_ticks_usec()); - hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_unix_time(), hash); - hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash); - hash = hash_djb2_one_32( - (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap - hash = hash_djb2_one_32( - (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack - - hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion - } - - return hash; -} - int ENetMultiplayerPeer::get_unique_id() const { - ERR_FAIL_COND_V_MSG(!active, 0, "The multiplayer instance isn't currently active."); + ERR_FAIL_COND_V_MSG(!_is_active(), 0, "The multiplayer instance isn't currently active."); return unique_id; } void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enable) { refuse_connections = p_enable; #ifdef GODOT_ENET - if (active) { - enet_host_refuse_new_connections(host, p_enable); + if (_is_active()) { + for (KeyValue<int, Ref<ENetConnection>> &E : hosts) { + E.value->refuse_new_connections(p_enable); + } } #endif } @@ -647,244 +547,122 @@ bool ENetMultiplayerPeer::is_refusing_new_connections() const { return refuse_connections; } -void ENetMultiplayerPeer::set_compression_mode(CompressionMode p_mode) { - compression_mode = p_mode; +void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) { + ERR_FAIL_COND_MSG(_is_active(), "Server relaying can't be toggled while the multiplayer instance is active."); + + server_relay = p_enabled; } -ENetMultiplayerPeer::CompressionMode ENetMultiplayerPeer::get_compression_mode() const { - return compression_mode; +bool ENetMultiplayerPeer::is_server_relay_enabled() const { + return server_relay; } -size_t ENetMultiplayerPeer::enet_compress(void *context, const ENetBuffer *inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 *outData, size_t outLimit) { - ENetMultiplayerPeer *enet = (ENetMultiplayerPeer *)(context); +Ref<ENetConnection> ENetMultiplayerPeer::get_host() const { + ERR_FAIL_COND_V(!_is_active(), nullptr); + ERR_FAIL_COND_V(active_mode == MODE_MESH, nullptr); + return hosts[0]; +} - if (size_t(enet->src_compressor_mem.size()) < inLimit) { - enet->src_compressor_mem.resize(inLimit); - } +Ref<ENetPacketPeer> ENetMultiplayerPeer::get_peer(int p_id) const { + ERR_FAIL_COND_V(!_is_active(), nullptr); + ERR_FAIL_COND_V(!peers.has(p_id), nullptr); + ERR_FAIL_COND_V(active_mode == MODE_CLIENT && p_id != 1, nullptr); + return peers[p_id]; +} - int total = inLimit; - int ofs = 0; - while (total) { - for (size_t i = 0; i < inBufferCount; i++) { - int to_copy = MIN(total, int(inBuffers[i].dataLength)); - memcpy(&enet->src_compressor_mem.write[ofs], inBuffers[i].data, to_copy); - ofs += to_copy; - total -= to_copy; - } +void ENetMultiplayerPeer::_destroy_unused(ENetPacket *p_packet) { + if (p_packet->referenceCount == 0) { + enet_packet_destroy(p_packet); } +} - Compression::Mode mode; +void ENetMultiplayerPeer::_relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet) { + if (p_to == 0) { + // Re-send to everyone but sender :| + for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (E.key == p_from) { + continue; + } - switch (enet->compression_mode) { - case COMPRESS_FASTLZ: { - mode = Compression::MODE_FASTLZ; - } break; - case COMPRESS_ZLIB: { - mode = Compression::MODE_DEFLATE; - } break; - case COMPRESS_ZSTD: { - mode = Compression::MODE_ZSTD; - } break; - default: { - ERR_FAIL_V_MSG(0, vformat("Invalid ENet compression mode: %d", enet->compression_mode)); + E.value->send(p_channel, p_packet); } - } - - int req_size = Compression::get_max_compressed_buffer_size(ofs, mode); - if (enet->dst_compressor_mem.size() < req_size) { - enet->dst_compressor_mem.resize(req_size); - } - int ret = Compression::compress(enet->dst_compressor_mem.ptrw(), enet->src_compressor_mem.ptr(), ofs, mode); - - if (ret < 0) { - return 0; - } - - if (ret > int(outLimit)) { - return 0; // Do not bother - } - - memcpy(outData, enet->dst_compressor_mem.ptr(), ret); - - return ret; -} + } else if (p_to < 0) { + // Re-send to everyone but excluded and sender. + for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (E.key == p_from || E.key == -p_to) { // Do not resend to self, also do not send to excluded + continue; + } -size_t ENetMultiplayerPeer::enet_decompress(void *context, const enet_uint8 *inData, size_t inLimit, enet_uint8 *outData, size_t outLimit) { - ENetMultiplayerPeer *enet = (ENetMultiplayerPeer *)(context); - int ret = -1; - switch (enet->compression_mode) { - case COMPRESS_FASTLZ: { - ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_FASTLZ); - } break; - case COMPRESS_ZLIB: { - ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_DEFLATE); - } break; - case COMPRESS_ZSTD: { - ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_ZSTD); - } break; - default: { + E.value->send(p_channel, p_packet); } - } - if (ret < 0) { - return 0; } else { - return ret; + // To someone else, specifically + ERR_FAIL_COND(!peers.has(p_to)); + ENetPacket *packet = enet_packet_create(p_packet->data, p_packet->dataLength, p_packet->flags); + peers[p_to]->send(p_channel, packet); } } -void ENetMultiplayerPeer::_setup_compressor() { - switch (compression_mode) { - case COMPRESS_NONE: { - enet_host_compress(host, nullptr); - } break; - case COMPRESS_RANGE_CODER: { - enet_host_compress_with_range_coder(host); - } break; - case COMPRESS_FASTLZ: - case COMPRESS_ZLIB: - case COMPRESS_ZSTD: { - enet_host_compress(host, &enet_compressor); - } break; +void ENetMultiplayerPeer::_notify_peers(int p_id, bool p_connected) { + if (p_connected) { + ERR_FAIL_COND(!peers.has(p_id)); + // Someone connected, notify all the peers available. + Ref<ENetPacketPeer> peer = peers[p_id]; + ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); + encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); + encode_uint32(p_id, &packet->data[4]); + for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (E.key == p_id) { + continue; + } + // Send new peer to existing peer. + E.value->send(SYSCH_CONFIG, packet); + // Send existing peer to new peer. + // This packet will be automatically destroyed by ENet after send. + ENetPacket *packet2 = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); + encode_uint32(SYSMSG_ADD_PEER, &packet2->data[0]); + encode_uint32(E.key, &packet2->data[4]); + peer->send(SYSCH_CONFIG, packet2); + } + _destroy_unused(packet); + } else { + // Server just received a client disconnect and is in relay mode, notify everyone else. + ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); + encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); + encode_uint32(p_id, &packet->data[4]); + for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { + if (E.key == p_id) { + continue; + } + E.value->send(SYSCH_CONFIG, packet); + } + _destroy_unused(packet); } } -void ENetMultiplayerPeer::enet_compressor_destroy(void *context) { - // Nothing to do -} - -IPAddress ENetMultiplayerPeer::get_peer_address(int p_peer_id) const { - ERR_FAIL_COND_V_MSG(!peer_map.has(p_peer_id), IPAddress(), vformat("Peer ID %d not found in the list of peers.", p_peer_id)); - ERR_FAIL_COND_V_MSG(!is_server() && p_peer_id != 1, IPAddress(), "Can't get the address of peers other than the server (ID -1) when acting as a client."); - ERR_FAIL_COND_V_MSG(peer_map[p_peer_id] == nullptr, IPAddress(), vformat("Peer ID %d found in the list of peers, but is null.", p_peer_id)); - - IPAddress out; -#ifdef GODOT_ENET - out.set_ipv6((uint8_t *)&(peer_map[p_peer_id]->address.host)); -#else - out.set_ipv4((uint8_t *)&(peer_map[p_peer_id]->address.host)); -#endif - - return out; -} - -int ENetMultiplayerPeer::get_peer_port(int p_peer_id) const { - ERR_FAIL_COND_V_MSG(!peer_map.has(p_peer_id), 0, vformat("Peer ID %d not found in the list of peers.", p_peer_id)); - ERR_FAIL_COND_V_MSG(!is_server() && p_peer_id != 1, 0, "Can't get the address of peers other than the server (ID -1) when acting as a client."); - ERR_FAIL_COND_V_MSG(peer_map[p_peer_id] == nullptr, 0, vformat("Peer ID %d found in the list of peers, but is null.", p_peer_id)); -#ifdef GODOT_ENET - return peer_map[p_peer_id]->address.port; -#else - return peer_map[p_peer_id]->address.port; -#endif -} - -int ENetMultiplayerPeer::get_local_port() const { - ERR_FAIL_COND_V_MSG(!active || !host, 0, "The multiplayer instance isn't currently active."); - return host->address.port; -} - -void ENetMultiplayerPeer::set_peer_timeout(int p_peer_id, int p_timeout_limit, int p_timeout_min, int p_timeout_max) { - ERR_FAIL_COND_MSG(!peer_map.has(p_peer_id), vformat("Peer ID %d not found in the list of peers.", p_peer_id)); - ERR_FAIL_COND_MSG(!is_server() && p_peer_id != 1, "Can't change the timeout of peers other then the server when acting as a client."); - ERR_FAIL_COND_MSG(peer_map[p_peer_id] == nullptr, vformat("Peer ID %d found in the list of peers, but is null.", p_peer_id)); - ERR_FAIL_COND_MSG(p_timeout_limit > p_timeout_min || p_timeout_min > p_timeout_max, "Timeout limit must be less than minimum timeout, which itself must be less then maximum timeout"); - enet_peer_timeout(peer_map[p_peer_id], p_timeout_limit, p_timeout_min, p_timeout_max); -} - -void ENetMultiplayerPeer::set_transfer_channel(int p_channel) { - ERR_FAIL_COND_MSG(p_channel < -1 || p_channel >= channel_count, vformat("The transfer channel must be set between 0 and %d, inclusive (got %d).", channel_count - 1, p_channel)); - ERR_FAIL_COND_MSG(p_channel == SYSCH_CONFIG, vformat("The channel %d is reserved.", SYSCH_CONFIG)); - transfer_channel = p_channel; -} - -int ENetMultiplayerPeer::get_transfer_channel() const { - return transfer_channel; -} - -void ENetMultiplayerPeer::set_channel_count(int p_channel) { - ERR_FAIL_COND_MSG(active, "The channel count can't be set while the multiplayer instance is active."); - ERR_FAIL_COND_MSG(p_channel < SYSCH_MAX, vformat("The channel count must be greater than or equal to %d to account for reserved channels (got %d).", SYSCH_MAX, p_channel)); - channel_count = p_channel; -} - -int ENetMultiplayerPeer::get_channel_count() const { - return channel_count; -} - -void ENetMultiplayerPeer::set_always_ordered(bool p_ordered) { - always_ordered = p_ordered; -} - -bool ENetMultiplayerPeer::is_always_ordered() const { - return always_ordered; -} - -void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) { - ERR_FAIL_COND_MSG(active, "Server relaying can't be toggled while the multiplayer instance is active."); - - server_relay = p_enabled; -} - -bool ENetMultiplayerPeer::is_server_relay_enabled() const { - return server_relay; -} - void ENetMultiplayerPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "in_bandwidth", "out_bandwidth"), &ENetMultiplayerPeer::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0)); - ClassDB::bind_method(D_METHOD("create_client", "address", "port", "in_bandwidth", "out_bandwidth", "local_port"), &ENetMultiplayerPeer::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetMultiplayerPeer::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("create_client", "address", "port", "channel_count", "in_bandwidth", "out_bandwidth", "local_port"), &ENetMultiplayerPeer::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("create_mesh", "unique_id"), &ENetMultiplayerPeer::create_mesh); + ClassDB::bind_method(D_METHOD("add_mesh_peer", "peer_id", "host"), &ENetMultiplayerPeer::add_mesh_peer); ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &ENetMultiplayerPeer::close_connection, DEFVAL(100)); - ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "now"), &ENetMultiplayerPeer::disconnect_peer, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("set_compression_mode", "mode"), &ENetMultiplayerPeer::set_compression_mode); - ClassDB::bind_method(D_METHOD("get_compression_mode"), &ENetMultiplayerPeer::get_compression_mode); ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &ENetMultiplayerPeer::set_bind_ip); - ClassDB::bind_method(D_METHOD("set_dtls_enabled", "enabled"), &ENetMultiplayerPeer::set_dtls_enabled); - ClassDB::bind_method(D_METHOD("is_dtls_enabled"), &ENetMultiplayerPeer::is_dtls_enabled); - ClassDB::bind_method(D_METHOD("set_dtls_key", "key"), &ENetMultiplayerPeer::set_dtls_key); - ClassDB::bind_method(D_METHOD("set_dtls_certificate", "certificate"), &ENetMultiplayerPeer::set_dtls_certificate); - ClassDB::bind_method(D_METHOD("set_dtls_verify_enabled", "enabled"), &ENetMultiplayerPeer::set_dtls_verify_enabled); - ClassDB::bind_method(D_METHOD("is_dtls_verify_enabled"), &ENetMultiplayerPeer::is_dtls_verify_enabled); - ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &ENetMultiplayerPeer::get_peer_address); - ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &ENetMultiplayerPeer::get_peer_port); - ClassDB::bind_method(D_METHOD("get_local_port"), &ENetMultiplayerPeer::get_local_port); - ClassDB::bind_method(D_METHOD("set_peer_timeout", "id", "timeout_limit", "timeout_min", "timeout_max"), &ENetMultiplayerPeer::set_peer_timeout); - - ClassDB::bind_method(D_METHOD("get_packet_channel"), &ENetMultiplayerPeer::get_packet_channel); - ClassDB::bind_method(D_METHOD("get_last_packet_channel"), &ENetMultiplayerPeer::get_last_packet_channel); - ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &ENetMultiplayerPeer::set_transfer_channel); - ClassDB::bind_method(D_METHOD("get_transfer_channel"), &ENetMultiplayerPeer::get_transfer_channel); - ClassDB::bind_method(D_METHOD("set_channel_count", "channels"), &ENetMultiplayerPeer::set_channel_count); - ClassDB::bind_method(D_METHOD("get_channel_count"), &ENetMultiplayerPeer::get_channel_count); - ClassDB::bind_method(D_METHOD("set_always_ordered", "ordered"), &ENetMultiplayerPeer::set_always_ordered); - ClassDB::bind_method(D_METHOD("is_always_ordered"), &ENetMultiplayerPeer::is_always_ordered); + ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &ENetMultiplayerPeer::set_server_relay_enabled); ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &ENetMultiplayerPeer::is_server_relay_enabled); + ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host); + ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer); - ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel"), "set_transfer_channel", "get_transfer_channel"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "channel_count"), "set_channel_count", "get_channel_count"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "always_ordered"), "set_always_ordered", "is_always_ordered"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dtls_verify"), "set_dtls_verify_enabled", "is_dtls_verify_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_dtls"), "set_dtls_enabled", "is_dtls_enabled"); - - BIND_ENUM_CONSTANT(COMPRESS_NONE); - BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER); - BIND_ENUM_CONSTANT(COMPRESS_FASTLZ); - BIND_ENUM_CONSTANT(COMPRESS_ZLIB); - BIND_ENUM_CONSTANT(COMPRESS_ZSTD); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host"); } ENetMultiplayerPeer::ENetMultiplayerPeer() { - enet_compressor.context = this; - enet_compressor.compress = enet_compress; - enet_compressor.decompress = enet_decompress; - enet_compressor.destroy = enet_compressor_destroy; - bind_ip = IPAddress("*"); } ENetMultiplayerPeer::~ENetMultiplayerPeer() { - if (active) { + if (_is_active()) { close_connection(); } } @@ -896,31 +674,3 @@ void ENetMultiplayerPeer::set_bind_ip(const IPAddress &p_ip) { bind_ip = p_ip; } - -void ENetMultiplayerPeer::set_dtls_enabled(bool p_enabled) { - ERR_FAIL_COND(active); - dtls_enabled = p_enabled; -} - -bool ENetMultiplayerPeer::is_dtls_enabled() const { - return dtls_enabled; -} - -void ENetMultiplayerPeer::set_dtls_verify_enabled(bool p_enabled) { - ERR_FAIL_COND(active); - dtls_verify = p_enabled; -} - -bool ENetMultiplayerPeer::is_dtls_verify_enabled() const { - return dtls_verify; -} - -void ENetMultiplayerPeer::set_dtls_key(Ref<CryptoKey> p_key) { - ERR_FAIL_COND(active); - dtls_key = p_key; -} - -void ENetMultiplayerPeer::set_dtls_certificate(Ref<X509Certificate> p_cert) { - ERR_FAIL_COND(active); - dtls_cert = p_cert; -} diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h index 63f2ec5870..703396d0cb 100644 --- a/modules/enet/enet_multiplayer_peer.h +++ b/modules/enet/enet_multiplayer_peer.h @@ -32,23 +32,14 @@ #define NETWORKED_MULTIPLAYER_ENET_H #include "core/crypto/crypto.h" -#include "core/io/compression.h" #include "core/io/multiplayer_peer.h" +#include "enet_connection.h" #include <enet/enet.h> class ENetMultiplayerPeer : public MultiplayerPeer { GDCLASS(ENetMultiplayerPeer, MultiplayerPeer); -public: - enum CompressionMode { - COMPRESS_NONE, - COMPRESS_RANGE_CODER, - COMPRESS_FASTLZ, - COMPRESS_ZLIB, - COMPRESS_ZSTD - }; - private: enum { SYSMSG_ADD_PEER, @@ -62,27 +53,27 @@ private: SYSCH_MAX }; - bool active = false; - bool server = false; + enum Mode { + MODE_NONE, + MODE_SERVER, + MODE_CLIENT, + MODE_MESH, + }; + + Mode active_mode = MODE_NONE; uint32_t unique_id = 0; int target_peer = 0; TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; - int transfer_channel = -1; - int channel_count = SYSCH_MAX; - bool always_ordered = false; - - ENetEvent event; - ENetPeer *peer = nullptr; - ENetHost *host = nullptr; bool refuse_connections = false; bool server_relay = true; ConnectionStatus connection_status = CONNECTION_DISCONNECTED; - Map<int, ENetPeer *> peer_map; + Map<int, Ref<ENetConnection>> hosts; + Map<int, Ref<ENetPacketPeer>> peers; struct Packet { ENetPacket *packet = nullptr; @@ -90,31 +81,21 @@ private: int channel = 0; }; - CompressionMode compression_mode = COMPRESS_RANGE_CODER; - List<Packet> incoming_packets; Packet current_packet; - uint32_t _gen_unique_id() const; void _pop_current_packet(); - - Vector<uint8_t> src_compressor_mem; - Vector<uint8_t> dst_compressor_mem; - - ENetCompressor enet_compressor; - static size_t enet_compress(void *context, const ENetBuffer *inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 *outData, size_t outLimit); - static size_t enet_decompress(void *context, const enet_uint8 *inData, size_t inLimit, enet_uint8 *outData, size_t outLimit); - static void enet_compressor_destroy(void *context); - void _setup_compressor(); + bool _poll_server(); + bool _poll_client(); + bool _poll_mesh(); + void _relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet); + void _notify_peers(int p_id, bool p_connected); + void _destroy_unused(ENetPacket *p_packet); + _FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; } IPAddress bind_ip; - bool dtls_enabled = false; - Ref<CryptoKey> dtls_key; - Ref<X509Certificate> dtls_cert; - bool dtls_verify = true; - protected: static void _bind_methods(); @@ -125,13 +106,10 @@ public: virtual int get_packet_peer() const override; - virtual IPAddress get_peer_address(int p_peer_id) const; - virtual int get_peer_port(int p_peer_id) const; - virtual int get_local_port() const; - void set_peer_timeout(int p_peer_id, int p_timeout_limit, int p_timeout_min, int p_timeout_max); - - Error create_server(int p_port, int p_max_clients = 32, int p_in_bandwidth = 0, int p_out_bandwidth = 0); - Error create_client(const String &p_address, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_local_port = 0); + Error create_server(int p_port, int p_max_clients = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0); + Error create_client(const String &p_address, int p_port, int p_channel_count = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_local_port = 0); + Error create_mesh(int p_id); + Error add_mesh_peer(int p_id, Ref<ENetConnection> p_host); void close_connection(uint32_t wait_usec = 100); @@ -154,32 +132,15 @@ public: virtual int get_unique_id() const override; - void set_compression_mode(CompressionMode p_mode); - CompressionMode get_compression_mode() const; - - int get_packet_channel() const; - int get_last_packet_channel() const; - void set_transfer_channel(int p_channel); - int get_transfer_channel() const; - void set_channel_count(int p_channel); - int get_channel_count() const; - void set_always_ordered(bool p_ordered); - bool is_always_ordered() const; + void set_bind_ip(const IPAddress &p_ip); void set_server_relay_enabled(bool p_enabled); bool is_server_relay_enabled() const; + Ref<ENetConnection> get_host() const; + Ref<ENetPacketPeer> get_peer(int p_id) const; + ENetMultiplayerPeer(); ~ENetMultiplayerPeer(); - - void set_bind_ip(const IPAddress &p_ip); - void set_dtls_enabled(bool p_enabled); - bool is_dtls_enabled() const; - void set_dtls_verify_enabled(bool p_enabled); - bool is_dtls_verify_enabled() const; - void set_dtls_key(Ref<CryptoKey> p_key); - void set_dtls_certificate(Ref<X509Certificate> p_cert); }; -VARIANT_ENUM_CAST(ENetMultiplayerPeer::CompressionMode); - #endif // NETWORKED_MULTIPLAYER_ENET_H diff --git a/modules/enet/enet_packet_peer.cpp b/modules/enet/enet_packet_peer.cpp new file mode 100644 index 0000000000..d7d2ec9ebe --- /dev/null +++ b/modules/enet/enet_packet_peer.cpp @@ -0,0 +1,263 @@ +/*************************************************************************/ +/* enet_packet_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "enet_packet_peer.h" + +void ENetPacketPeer::peer_disconnect(int p_data) { + ERR_FAIL_COND(!peer); + enet_peer_disconnect(peer, p_data); +} + +void ENetPacketPeer::peer_disconnect_later(int p_data) { + ERR_FAIL_COND(!peer); + enet_peer_disconnect_later(peer, p_data); +} + +void ENetPacketPeer::peer_disconnect_now(int p_data) { + ERR_FAIL_COND(!peer); + enet_peer_disconnect_now(peer, p_data); + _on_disconnect(); +} + +void ENetPacketPeer::ping() { + ERR_FAIL_COND(!peer); + enet_peer_ping(peer); +} + +void ENetPacketPeer::ping_interval(int p_interval) { + ERR_FAIL_COND(!peer); + enet_peer_ping_interval(peer, p_interval); +} + +int ENetPacketPeer::send(uint8_t p_channel, ENetPacket *p_packet) { + ERR_FAIL_COND_V(peer == nullptr, -1); + ERR_FAIL_COND_V(p_packet == nullptr, -1); + ERR_FAIL_COND_V_MSG(p_channel >= peer->channelCount, -1, vformat("Unable to send packet on channel %d, max channels: %d", p_channel, (int)peer->channelCount)); + return enet_peer_send(peer, p_channel, p_packet); +} + +void ENetPacketPeer::reset() { + ERR_FAIL_COND_MSG(peer == nullptr, "Peer not connected"); + enet_peer_reset(peer); + _on_disconnect(); +} + +void ENetPacketPeer::throttle_configure(int p_interval, int p_acceleration, int p_deceleration) { + ERR_FAIL_COND_MSG(peer == nullptr, "Peer not connected"); + enet_peer_throttle_configure(peer, p_interval, p_acceleration, p_deceleration); +} + +void ENetPacketPeer::set_timeout(int p_timeout, int p_timeout_min, int p_timeout_max) { + ERR_FAIL_COND_MSG(peer == nullptr, "Peer not connected"); + ERR_FAIL_COND_MSG(p_timeout > p_timeout_min || p_timeout_min > p_timeout_max, "Timeout limit must be less than minimum timeout, which itself must be less then maximum timeout"); + enet_peer_timeout(peer, p_timeout, p_timeout_min, p_timeout_max); +} + +int ENetPacketPeer::get_max_packet_size() const { + return 1 << 24; +} + +int ENetPacketPeer::get_available_packet_count() const { + return packet_queue.size(); +} + +Error ENetPacketPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + ERR_FAIL_COND_V(!peer, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!packet_queue.size(), ERR_UNAVAILABLE); + if (last_packet) { + enet_packet_destroy(last_packet); + last_packet = nullptr; + } + last_packet = packet_queue.front()->get(); + packet_queue.pop_front(); + *r_buffer = (const uint8_t *)(last_packet->data); + r_buffer_size = last_packet->dataLength; + return OK; +} + +Error ENetPacketPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(!peer, ERR_UNCONFIGURED); + ENetPacket *packet = enet_packet_create(p_buffer, p_buffer_size, ENET_PACKET_FLAG_RELIABLE); + return send(0, packet) < 0 ? FAILED : OK; +} + +IPAddress ENetPacketPeer::get_remote_address() const { + ERR_FAIL_COND_V(!peer, IPAddress()); + IPAddress out; +#ifdef GODOT_ENET + out.set_ipv6((uint8_t *)&(peer->address.host)); +#else + out.set_ipv4((uint8_t *)&(peer->address.host)); +#endif + return out; +} + +int ENetPacketPeer::get_remote_port() const { + ERR_FAIL_COND_V(!peer, 0); + return peer->address.port; +} + +bool ENetPacketPeer::is_active() const { + return peer != nullptr; +} + +double ENetPacketPeer::get_statistic(PeerStatistic p_stat) { + ERR_FAIL_COND_V(!peer, 0); + switch (p_stat) { + case PEER_PACKET_LOSS: + return peer->packetLoss; + case PEER_PACKET_LOSS_VARIANCE: + return peer->packetLossVariance; + case PEER_PACKET_LOSS_EPOCH: + return peer->packetLossEpoch; + case PEER_ROUND_TRIP_TIME: + return peer->roundTripTime; + case PEER_ROUND_TRIP_TIME_VARIANCE: + return peer->roundTripTimeVariance; + case PEER_LAST_ROUND_TRIP_TIME: + return peer->lastRoundTripTime; + case PEER_LAST_ROUND_TRIP_TIME_VARIANCE: + return peer->lastRoundTripTimeVariance; + case PEER_PACKET_THROTTLE: + return peer->packetThrottle; + case PEER_PACKET_THROTTLE_LIMIT: + return peer->packetThrottleLimit; + case PEER_PACKET_THROTTLE_COUNTER: + return peer->packetThrottleCounter; + case PEER_PACKET_THROTTLE_EPOCH: + return peer->packetThrottleEpoch; + case PEER_PACKET_THROTTLE_ACCELERATION: + return peer->packetThrottleAcceleration; + case PEER_PACKET_THROTTLE_DECELERATION: + return peer->packetThrottleDeceleration; + case PEER_PACKET_THROTTLE_INTERVAL: + return peer->packetThrottleInterval; + } + ERR_FAIL_V(0); +} + +ENetPacketPeer::PeerState ENetPacketPeer::get_state() const { + if (!is_active()) { + return STATE_DISCONNECTED; + } + return (PeerState)peer->state; +} + +int ENetPacketPeer::get_channels() const { + ERR_FAIL_COND_V_MSG(!peer, 0, "The ENetConnection instance isn't currently active."); + return peer->channelCount; +} + +void ENetPacketPeer::_on_disconnect() { + if (peer) { + peer->data = nullptr; + } + peer = nullptr; +} + +void ENetPacketPeer::_queue_packet(ENetPacket *p_packet) { + ERR_FAIL_COND(!peer); + packet_queue.push_back(p_packet); +} + +Error ENetPacketPeer::_send(int p_channel, PackedByteArray p_packet, int p_flags) { + ERR_FAIL_COND_V_MSG(peer == nullptr, ERR_UNCONFIGURED, "Peer not connected"); + ERR_FAIL_COND_V_MSG(p_channel < 0 || p_channel > (int)peer->channelCount, ERR_INVALID_PARAMETER, "Invalid channel"); + ERR_FAIL_COND_V_MSG(p_flags & ~FLAG_ALLOWED, ERR_INVALID_PARAMETER, "Invalid flags"); + ENetPacket *packet = enet_packet_create(p_packet.ptr(), p_packet.size(), p_flags); + return send(p_channel, packet) == 0 ? OK : FAILED; +} + +void ENetPacketPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("peer_disconnect", "data"), &ENetPacketPeer::peer_disconnect, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("peer_disconnect_later", "data"), &ENetPacketPeer::peer_disconnect_later, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("peer_disconnect_now", "data"), &ENetPacketPeer::peer_disconnect_now, DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("ping"), &ENetPacketPeer::ping); + ClassDB::bind_method(D_METHOD("ping_interval", "ping_interval"), &ENetPacketPeer::ping_interval); + ClassDB::bind_method(D_METHOD("reset"), &ENetPacketPeer::reset); + ClassDB::bind_method(D_METHOD("send", "channel", "packet", "flags"), &ENetPacketPeer::_send); + ClassDB::bind_method(D_METHOD("throttle_configure", "interval", "acceleration", "deceleration"), &ENetPacketPeer::throttle_configure); + ClassDB::bind_method(D_METHOD("set_timeout", "timeout", "timeout_min", "timeout_max"), &ENetPacketPeer::set_timeout); + ClassDB::bind_method(D_METHOD("get_statistic", "statistic"), &ENetPacketPeer::get_statistic); + ClassDB::bind_method(D_METHOD("get_state"), &ENetPacketPeer::get_state); + ClassDB::bind_method(D_METHOD("get_channels"), &ENetPacketPeer::get_channels); + ClassDB::bind_method(D_METHOD("is_active"), &ENetPacketPeer::is_active); + + BIND_ENUM_CONSTANT(STATE_DISCONNECTED); + BIND_ENUM_CONSTANT(STATE_CONNECTING); + BIND_ENUM_CONSTANT(STATE_ACKNOWLEDGING_CONNECT); + BIND_ENUM_CONSTANT(STATE_CONNECTION_PENDING); + BIND_ENUM_CONSTANT(STATE_CONNECTION_SUCCEEDED); + BIND_ENUM_CONSTANT(STATE_CONNECTED); + BIND_ENUM_CONSTANT(STATE_DISCONNECT_LATER); + BIND_ENUM_CONSTANT(STATE_DISCONNECTING); + BIND_ENUM_CONSTANT(STATE_ACKNOWLEDGING_DISCONNECT); + BIND_ENUM_CONSTANT(STATE_ZOMBIE); + + BIND_ENUM_CONSTANT(PEER_PACKET_LOSS); + BIND_ENUM_CONSTANT(PEER_PACKET_LOSS_VARIANCE); + BIND_ENUM_CONSTANT(PEER_PACKET_LOSS_EPOCH); + BIND_ENUM_CONSTANT(PEER_ROUND_TRIP_TIME); + BIND_ENUM_CONSTANT(PEER_ROUND_TRIP_TIME_VARIANCE); + BIND_ENUM_CONSTANT(PEER_LAST_ROUND_TRIP_TIME); + BIND_ENUM_CONSTANT(PEER_LAST_ROUND_TRIP_TIME_VARIANCE); + BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE); + BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_LIMIT); + BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_COUNTER); + BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_EPOCH); + BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_ACCELERATION); + BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_DECELERATION); + BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_INTERVAL); + + BIND_CONSTANT(PACKET_LOSS_SCALE); + BIND_CONSTANT(PACKET_THROTTLE_SCALE); + + BIND_CONSTANT(FLAG_RELIABLE); + BIND_CONSTANT(FLAG_UNSEQUENCED); + BIND_CONSTANT(FLAG_UNRELIABLE_FRAGMENT); +} + +ENetPacketPeer::ENetPacketPeer(ENetPeer *p_peer) { + peer = p_peer; + peer->data = this; +} + +ENetPacketPeer::~ENetPacketPeer() { + _on_disconnect(); + if (last_packet) { + enet_packet_destroy(last_packet); + last_packet = nullptr; + } + for (List<ENetPacket *>::Element *E = packet_queue.front(); E; E = E->next()) { + enet_packet_destroy(E->get()); + } + packet_queue.clear(); +} diff --git a/modules/enet/enet_packet_peer.h b/modules/enet/enet_packet_peer.h new file mode 100644 index 0000000000..9af004de2f --- /dev/null +++ b/modules/enet/enet_packet_peer.h @@ -0,0 +1,131 @@ +/*************************************************************************/ +/* enet_packet_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 ENET_PACKET_PEER_H +#define ENET_PACKET_PEER_H + +#include "core/io/packet_peer.h" + +#include <enet/enet.h> + +class ENetPacketPeer : public PacketPeer { + GDCLASS(ENetPacketPeer, PacketPeer); + +private: + ENetPeer *peer = nullptr; + List<ENetPacket *> packet_queue; + ENetPacket *last_packet = nullptr; + + static void _bind_methods(); + Error _send(int p_channel, PackedByteArray p_packet, int p_flags); + +protected: + friend class ENetConnection; + // Internally used by ENetConnection during service, destroy, etc. + void _on_disconnect(); + void _queue_packet(ENetPacket *p_packet); + +public: + enum { + PACKET_THROTTLE_SCALE = ENET_PEER_PACKET_THROTTLE_SCALE, + PACKET_LOSS_SCALE = ENET_PEER_PACKET_LOSS_SCALE, + }; + + enum { + FLAG_RELIABLE = ENET_PACKET_FLAG_RELIABLE, + FLAG_UNSEQUENCED = ENET_PACKET_FLAG_UNSEQUENCED, + FLAG_UNRELIABLE_FRAGMENT = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, + FLAG_ALLOWED = ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, + }; + + enum PeerState { + STATE_DISCONNECTED = ENET_PEER_STATE_DISCONNECTED, + STATE_CONNECTING = ENET_PEER_STATE_CONNECTING, + STATE_ACKNOWLEDGING_CONNECT = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT, + STATE_CONNECTION_PENDING = ENET_PEER_STATE_CONNECTION_PENDING, + STATE_CONNECTION_SUCCEEDED = ENET_PEER_STATE_CONNECTION_SUCCEEDED, + STATE_CONNECTED = ENET_PEER_STATE_CONNECTED, + STATE_DISCONNECT_LATER = ENET_PEER_STATE_DISCONNECT_LATER, + STATE_DISCONNECTING = ENET_PEER_STATE_DISCONNECTING, + STATE_ACKNOWLEDGING_DISCONNECT = ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT, + STATE_ZOMBIE = ENET_PEER_STATE_ZOMBIE, + }; + + enum PeerStatistic { + PEER_PACKET_LOSS, + PEER_PACKET_LOSS_VARIANCE, + PEER_PACKET_LOSS_EPOCH, + PEER_ROUND_TRIP_TIME, + PEER_ROUND_TRIP_TIME_VARIANCE, + PEER_LAST_ROUND_TRIP_TIME, + PEER_LAST_ROUND_TRIP_TIME_VARIANCE, + PEER_PACKET_THROTTLE, + PEER_PACKET_THROTTLE_LIMIT, + PEER_PACKET_THROTTLE_COUNTER, + PEER_PACKET_THROTTLE_EPOCH, + PEER_PACKET_THROTTLE_ACCELERATION, + PEER_PACKET_THROTTLE_DECELERATION, + PEER_PACKET_THROTTLE_INTERVAL, + }; + + int get_max_packet_size() const override; + int get_available_packet_count() const override; + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + + void peer_disconnect(int p_data = 0); + void peer_disconnect_later(int p_data = 0); + void peer_disconnect_now(int p_data = 0); + + void ping(); + void ping_interval(int p_interval); + void reset(); + int send(uint8_t p_channel, ENetPacket *p_packet); + void throttle_configure(int interval, int acceleration, int deceleration); + void set_timeout(int p_timeout, int p_timeout_min, int p_timeout_max); + double get_statistic(PeerStatistic p_stat); + PeerState get_state() const; + int get_channels() const; + + // Extras + IPAddress get_remote_address() const; + int get_remote_port() const; + + // Used by ENetMultiplayer (TODO use meta? If only they where StringNames) + bool is_active() const; + + ENetPacketPeer(ENetPeer *p_peer); + ~ENetPacketPeer(); +}; + +VARIANT_ENUM_CAST(ENetPacketPeer::PeerState); +VARIANT_ENUM_CAST(ENetPacketPeer::PeerStatistic); + +#endif // ENET_PACKET_PEER_H diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp index 38870316e4..7570f5b643 100644 --- a/modules/enet/register_types.cpp +++ b/modules/enet/register_types.cpp @@ -30,7 +30,9 @@ #include "register_types.h" #include "core/error/error_macros.h" +#include "enet_connection.h" #include "enet_multiplayer_peer.h" +#include "enet_packet_peer.h" static bool enet_ok = false; @@ -42,6 +44,8 @@ void register_enet_types() { } GDREGISTER_CLASS(ENetMultiplayerPeer); + GDREGISTER_VIRTUAL_CLASS(ENetPacketPeer); + GDREGISTER_CLASS(ENetConnection); } void unregister_enet_types() { diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml index 4f1530598c..e4c5d34a2c 100644 --- a/modules/gdnative/doc_classes/GDNative.xml +++ b/modules/gdnative/doc_classes/GDNative.xml @@ -8,26 +8,20 @@ </tutorials> <methods> <method name="call_native"> - <return type="Variant"> - </return> - <argument index="0" name="calling_type" type="StringName"> - </argument> - <argument index="1" name="procedure_name" type="StringName"> - </argument> - <argument index="2" name="arguments" type="Array"> - </argument> + <return type="Variant" /> + <argument index="0" name="calling_type" type="StringName" /> + <argument index="1" name="procedure_name" type="StringName" /> + <argument index="2" name="arguments" type="Array" /> <description> </description> </method> <method name="initialize"> - <return type="bool"> - </return> + <return type="bool" /> <description> </description> </method> <method name="terminate"> - <return type="bool"> - </return> + <return type="bool" /> <description> </description> </method> diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index 05cda05f9f..f84d4e60f3 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -12,15 +12,13 @@ </tutorials> <methods> <method name="get_current_dependencies" qualifiers="const"> - <return type="PackedStringArray"> - </return> + <return type="PackedStringArray" /> <description> Returns paths to all dependency libraries for the current platform and architecture. </description> </method> <method name="get_current_library_path" qualifiers="const"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the path to the dynamic library file for the current platform and architecture. </description> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index f2e9cac6dc..397d12a3a9 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -8,42 +8,34 @@ </tutorials> <methods> <method name="get_class_documentation" qualifiers="const"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the documentation string that was previously set with [code]godot_nativescript_set_class_documentation[/code]. </description> </method> <method name="get_method_documentation" qualifiers="const"> - <return type="String"> - </return> - <argument index="0" name="method" type="StringName"> - </argument> + <return type="String" /> + <argument index="0" name="method" type="StringName" /> <description> Returns the documentation string that was previously set with [code]godot_nativescript_set_method_documentation[/code]. </description> </method> <method name="get_property_documentation" qualifiers="const"> - <return type="String"> - </return> - <argument index="0" name="path" type="StringName"> - </argument> + <return type="String" /> + <argument index="0" name="path" type="StringName" /> <description> Returns the documentation string that was previously set with [code]godot_nativescript_set_property_documentation[/code]. </description> </method> <method name="get_signal_documentation" qualifiers="const"> - <return type="String"> - </return> - <argument index="0" name="signal_name" type="StringName"> - </argument> + <return type="String" /> + <argument index="0" name="signal_name" type="StringName" /> <description> Returns the documentation string that was previously set with [code]godot_nativescript_set_signal_documentation[/code]. </description> </method> <method name="new" qualifiers="vararg"> - <return type="Variant"> - </return> + <return type="Variant" /> <description> Constructs a new object of the base type with a script of this type already attached. [i]Note[/i]: Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension. diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml index 9616101090..8e28187482 100644 --- a/modules/gdnative/doc_classes/PluginScript.xml +++ b/modules/gdnative/doc_classes/PluginScript.xml @@ -8,8 +8,7 @@ </tutorials> <methods> <method name="new" qualifiers="vararg"> - <return type="Variant"> - </return> + <return type="Variant" /> <description> Returns a new instance of the script. </description> diff --git a/modules/gdnative/doc_classes/VideoStreamGDNative.xml b/modules/gdnative/doc_classes/VideoStreamGDNative.xml index 153988bad8..8b1a3210df 100644 --- a/modules/gdnative/doc_classes/VideoStreamGDNative.xml +++ b/modules/gdnative/doc_classes/VideoStreamGDNative.xml @@ -11,17 +11,14 @@ </tutorials> <methods> <method name="get_file"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the video file handled by this [VideoStreamGDNative]. </description> </method> <method name="set_file"> - <return type="void"> - </return> - <argument index="0" name="file" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="file" type="String" /> <description> Sets the video file that this [VideoStreamGDNative] resource handles. The supported extensions depend on the GDNative plugins used to expose video formats. </description> diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index e68deb070d..51b3452a3a 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -11,16 +11,11 @@ </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> + <return type="Color" /> + <argument index="0" name="r8" type="int" /> + <argument index="1" name="g8" type="int" /> + <argument index="2" name="b8" type="int" /> + <argument index="3" name="a8" type="int" default="255" /> <description> Returns a color constructed from integer red, green, blue, and alpha channels. Each channel should have 8 bits of information ranging from 0 to 255. [code]r8[/code] red channel @@ -33,12 +28,9 @@ </description> </method> <method name="assert"> - <return type="void"> - </return> - <argument index="0" name="condition" type="bool"> - </argument> - <argument index="1" name="message" type="String" default=""""> - </argument> + <return type="void" /> + <argument index="0" name="condition" type="bool" /> + <argument index="1" name="message" type="String" default="""" /> <description> Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users. [b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode. @@ -54,10 +46,8 @@ </description> </method> <method name="char"> - <return type="String"> - </return> - <argument index="0" name="char" type="int"> - </argument> + <return type="String" /> + <argument index="0" name="char" type="int" /> <description> Returns a character as a String of the given Unicode code point (which is compatible with ASCII code). [codeblock] @@ -68,12 +58,9 @@ </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> + <return type="Variant" /> + <argument index="0" name="what" type="Variant" /> + <argument index="1" name="type" type="int" /> <description> Converts from a type to another in the best way possible. The [code]type[/code] parameter uses the [enum Variant.Type] values. [codeblock] @@ -87,17 +74,14 @@ </description> </method> <method name="dict2inst"> - <return type="Object"> - </return> - <argument index="0" name="dictionary" type="Dictionary"> - </argument> + <return type="Object" /> + <argument index="0" name="dictionary" type="Dictionary" /> <description> Converts a dictionary (previously created with [method inst2dict]) back to an instance. Useful for deserializing. </description> </method> <method name="get_stack"> - <return type="Array"> - </return> + <return type="Array" /> <description> Returns an array of dictionaries representing the current call stack. [codeblock] @@ -117,10 +101,8 @@ </description> </method> <method name="inst2dict"> - <return type="Dictionary"> - </return> - <argument index="0" name="instance" type="Object"> - </argument> + <return type="Dictionary" /> + <argument index="0" name="instance" type="Object" /> <description> Returns the passed instance converted to a dictionary (useful for serializing). [codeblock] @@ -138,10 +120,8 @@ </description> </method> <method name="len"> - <return type="int"> - </return> - <argument index="0" name="var" type="Variant"> - </argument> + <return type="int" /> + <argument index="0" name="var" type="Variant" /> <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. @@ -152,10 +132,8 @@ </description> </method> <method name="load"> - <return type="Resource"> - </return> - <argument index="0" name="path" type="String"> - </argument> + <return type="Resource" /> + <argument index="0" name="path" type="String" /> <description> Loads a resource from the filesystem located at [code]path[/code]. The resource is loaded on the method call (unless it's referenced already elsewhere, e.g. in another script or in the scene), which might cause slight delay, especially when loading scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload]. [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script. @@ -168,10 +146,8 @@ </description> </method> <method name="preload"> - <return type="Resource"> - </return> - <argument index="0" name="path" type="String"> - </argument> + <return type="Resource" /> + <argument index="0" name="path" type="String" /> <description> Returns a [Resource] from the filesystem located at [code]path[/code]. The resource is loaded during script parsing, i.e. is loaded with the script and [method preload] effectively acts as a reference to that resource. Note that the method requires a constant path. If you want to load a resource from a dynamic/variable path, use [method load]. [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script. @@ -182,15 +158,13 @@ </description> </method> <method name="print_debug" qualifiers="vararg"> - <return type="void"> - </return> + <return type="void" /> <description> Like [method @GlobalScope.print], but prints only when used in debug mode. </description> </method> <method name="print_stack"> - <return type="void"> - </return> + <return type="void" /> <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: @@ -200,8 +174,7 @@ </description> </method> <method name="range" qualifiers="vararg"> - <return type="Array"> - </return> + <return type="Array" /> <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] @@ -218,8 +191,7 @@ </description> </method> <method name="str" qualifiers="vararg"> - <return type="String"> - </return> + <return type="String" /> <description> Converts one or more arguments to string in the best way possible. [codeblock] @@ -231,10 +203,8 @@ </description> </method> <method name="type_exists"> - <return type="bool"> - </return> - <argument index="0" name="type" type="StringName"> - </argument> + <return type="bool" /> + <argument index="0" name="type" type="StringName" /> <description> </description> </method> diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 631a102130..72738f027a 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -12,15 +12,13 @@ </tutorials> <methods> <method name="get_as_byte_code" qualifiers="const"> - <return type="PackedByteArray"> - </return> + <return type="PackedByteArray" /> <description> Returns byte code for the script source code. </description> </method> <method name="new" qualifiers="vararg"> - <return type="Variant"> - </return> + <return type="Variant" /> <description> Returns a new instance of the script. For example: diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml index 40563c9ac6..6e83cec252 100644 --- a/modules/gltf/doc_classes/GLTFSkeleton.xml +++ b/modules/gltf/doc_classes/GLTFSkeleton.xml @@ -8,50 +8,40 @@ </tutorials> <methods> <method name="get_bone_attachment"> - <return type="BoneAttachment3D"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="BoneAttachment3D" /> + <argument index="0" name="idx" type="int" /> <description> </description> </method> <method name="get_bone_attachment_count"> - <return type="int"> - </return> + <return type="int" /> <description> </description> </method> <method name="get_godot_bone_node"> - <return type="Dictionary"> - </return> + <return type="Dictionary" /> <description> </description> </method> <method name="get_godot_skeleton"> - <return type="Skeleton3D"> - </return> + <return type="Skeleton3D" /> <description> </description> </method> <method name="get_unique_names"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="set_godot_bone_node"> - <return type="void"> - </return> - <argument index="0" name="godot_bone_node" type="Dictionary"> - </argument> + <return type="void" /> + <argument index="0" name="godot_bone_node" type="Dictionary" /> <description> </description> </method> <method name="set_unique_names"> - <return type="void"> - </return> - <argument index="0" name="unique_names" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="unique_names" type="Array" /> <description> </description> </method> diff --git a/modules/gltf/doc_classes/GLTFSkin.xml b/modules/gltf/doc_classes/GLTFSkin.xml index e20e127e52..107ca960cd 100644 --- a/modules/gltf/doc_classes/GLTFSkin.xml +++ b/modules/gltf/doc_classes/GLTFSkin.xml @@ -8,44 +8,35 @@ </tutorials> <methods> <method name="get_inverse_binds"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_joint_i_to_bone_i"> - <return type="Dictionary"> - </return> + <return type="Dictionary" /> <description> </description> </method> <method name="get_joint_i_to_name"> - <return type="Dictionary"> - </return> + <return type="Dictionary" /> <description> </description> </method> <method name="set_inverse_binds"> - <return type="void"> - </return> - <argument index="0" name="inverse_binds" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="inverse_binds" type="Array" /> <description> </description> </method> <method name="set_joint_i_to_bone_i"> - <return type="void"> - </return> - <argument index="0" name="joint_i_to_bone_i" type="Dictionary"> - </argument> + <return type="void" /> + <argument index="0" name="joint_i_to_bone_i" type="Dictionary" /> <description> </description> </method> <method name="set_joint_i_to_name"> - <return type="void"> - </return> - <argument index="0" name="joint_i_to_name" type="Dictionary"> - </argument> + <return type="void" /> + <argument index="0" name="joint_i_to_name" type="Dictionary" /> <description> </description> </method> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index a7b5b7b43e..ae976fc04c 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -8,236 +8,185 @@ </tutorials> <methods> <method name="get_accessors"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_animation_player"> - <return type="AnimationPlayer"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="AnimationPlayer" /> + <argument index="0" name="idx" type="int" /> <description> </description> </method> <method name="get_animation_players_count"> - <return type="int"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="int" /> + <argument index="0" name="idx" type="int" /> <description> </description> </method> <method name="get_animations"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_buffer_views"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_cameras"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_images"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_lights"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_materials"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_meshes"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_nodes"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_scene_node"> - <return type="Node"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="Node" /> + <argument index="0" name="idx" type="int" /> <description> </description> </method> <method name="get_skeleton_to_node"> - <return type="Dictionary"> - </return> + <return type="Dictionary" /> <description> </description> </method> <method name="get_skeletons"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_skins"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_textures"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_unique_animation_names"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="get_unique_names"> - <return type="Array"> - </return> + <return type="Array" /> <description> </description> </method> <method name="set_accessors"> - <return type="void"> - </return> - <argument index="0" name="accessors" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="accessors" type="Array" /> <description> </description> </method> <method name="set_animations"> - <return type="void"> - </return> - <argument index="0" name="animations" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="animations" type="Array" /> <description> </description> </method> <method name="set_buffer_views"> - <return type="void"> - </return> - <argument index="0" name="buffer_views" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="buffer_views" type="Array" /> <description> </description> </method> <method name="set_cameras"> - <return type="void"> - </return> - <argument index="0" name="cameras" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="cameras" type="Array" /> <description> </description> </method> <method name="set_images"> - <return type="void"> - </return> - <argument index="0" name="images" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="images" type="Array" /> <description> </description> </method> <method name="set_lights"> - <return type="void"> - </return> - <argument index="0" name="lights" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="lights" type="Array" /> <description> </description> </method> <method name="set_materials"> - <return type="void"> - </return> - <argument index="0" name="materials" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="materials" type="Array" /> <description> </description> </method> <method name="set_meshes"> - <return type="void"> - </return> - <argument index="0" name="meshes" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="meshes" type="Array" /> <description> </description> </method> <method name="set_nodes"> - <return type="void"> - </return> - <argument index="0" name="nodes" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="nodes" type="Array" /> <description> </description> </method> <method name="set_skeleton_to_node"> - <return type="void"> - </return> - <argument index="0" name="skeleton_to_node" type="Dictionary"> - </argument> + <return type="void" /> + <argument index="0" name="skeleton_to_node" type="Dictionary" /> <description> </description> </method> <method name="set_skeletons"> - <return type="void"> - </return> - <argument index="0" name="skeletons" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="skeletons" type="Array" /> <description> </description> </method> <method name="set_skins"> - <return type="void"> - </return> - <argument index="0" name="skins" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="skins" type="Array" /> <description> </description> </method> <method name="set_textures"> - <return type="void"> - </return> - <argument index="0" name="textures" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="textures" type="Array" /> <description> </description> </method> <method name="set_unique_animation_names"> - <return type="void"> - </return> - <argument index="0" name="unique_animation_names" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="unique_animation_names" type="Array" /> <description> </description> </method> <method name="set_unique_names"> - <return type="void"> - </return> - <argument index="0" name="unique_names" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="unique_names" type="Array" /> <description> </description> </method> diff --git a/modules/gltf/doc_classes/PackedSceneGLTF.xml b/modules/gltf/doc_classes/PackedSceneGLTF.xml index a22111e9b7..d0136c6402 100644 --- a/modules/gltf/doc_classes/PackedSceneGLTF.xml +++ b/modules/gltf/doc_classes/PackedSceneGLTF.xml @@ -8,44 +8,29 @@ </tutorials> <methods> <method name="export_gltf"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="node" type="Node"> - </argument> - <argument index="1" name="path" type="String"> - </argument> - <argument index="2" name="flags" type="int" default="0"> - </argument> - <argument index="3" name="bake_fps" type="float" default="1000.0"> - </argument> + <return type="int" enum="Error" /> + <argument index="0" name="node" type="Node" /> + <argument index="1" name="path" type="String" /> + <argument index="2" name="flags" type="int" default="0" /> + <argument index="3" name="bake_fps" type="float" default="1000.0" /> <description> </description> </method> <method name="import_gltf_scene"> - <return type="Node"> - </return> - <argument index="0" name="path" type="String"> - </argument> - <argument index="1" name="flags" type="int" default="0"> - </argument> - <argument index="2" name="bake_fps" type="float" default="1000.0"> - </argument> - <argument index="3" name="state" type="GLTFState" default="null"> - </argument> + <return type="Node" /> + <argument index="0" name="path" type="String" /> + <argument index="1" name="flags" type="int" default="0" /> + <argument index="2" name="bake_fps" type="float" default="1000.0" /> + <argument index="3" name="state" type="GLTFState" default="null" /> <description> </description> </method> <method name="pack_gltf"> - <return type="void"> - </return> - <argument index="0" name="path" type="String"> - </argument> - <argument index="1" name="flags" type="int" default="0"> - </argument> - <argument index="2" name="bake_fps" type="float" default="1000.0"> - </argument> - <argument index="3" name="state" type="GLTFState" default="null"> - </argument> + <return type="void" /> + <argument index="0" name="path" type="String" /> + <argument index="1" name="flags" type="int" default="0" /> + <argument index="2" name="bake_fps" type="float" default="1000.0" /> + <argument index="3" name="state" type="GLTFState" default="null" /> <description> </description> </method> diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 30d023606f..be44f66423 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3002,24 +3002,31 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat Ref<Image> img; + // First we honor the mime types if they were defined. if (mimetype == "image/png") { // Load buffer as PNG. ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, ERR_UNAVAILABLE); img = Image::_png_mem_loader_func(data_ptr, data_size); } else if (mimetype == "image/jpeg") { // Loader buffer as JPEG. ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE); img = Image::_jpg_mem_loader_func(data_ptr, data_size); - } else { - // We can land here if we got an URI with base64-encoded data with application/* MIME type, - // and the optional mimeType property was not defined to tell us how to handle this data (or was invalid). - // So let's try PNG first, then JPEG. + } + + // If we didn't pass the above tests, we attempt loading as PNG and then + // JPEG directly. + // This covers URIs with base64-encoded data with application/* type but + // no optional mimeType property, or bufferViews with a bogus mimeType + // (e.g. `image/jpeg` but the data is actually PNG). + // That's not *exactly* what the spec mandates but this lets us be + // lenient with bogus glb files which do exist in production. + if (img.is_null()) { // Try PNG first. ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, ERR_UNAVAILABLE); img = Image::_png_mem_loader_func(data_ptr, data_size); - if (img.is_null()) { - ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE); - img = Image::_jpg_mem_loader_func(data_ptr, data_size); - } } - + if (img.is_null()) { // And then JPEG. + ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE); + img = Image::_jpg_mem_loader_func(data_ptr, data_size); + } + // Now we've done our best, fix your scenes. if (img.is_null()) { ERR_PRINT(vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype)); state->images.push_back(Ref<Texture2D>()); @@ -5531,7 +5538,10 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> { template <class T> T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { ERR_FAIL_COND_V(!p_values.size(), T()); - ERR_FAIL_COND_V(p_times.size() != p_values.size(), p_values[0]); + if (p_times.size() != p_values.size()) { + ERR_PRINT_ONCE("The interpolated values are not corresponding to its times."); + return p_values[0]; + } //could use binary search, worth it? int idx = -1; for (int i = 0; i < p_times.size(); i++) { diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index a2c8e8eabf..8ea7384658 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -17,119 +17,93 @@ </tutorials> <methods> <method name="clear"> - <return type="void"> - </return> + <return type="void" /> <description> Clear all cells. </description> </method> <method name="clear_baked_meshes"> - <return type="void"> - </return> + <return type="void" /> <description> </description> </method> <method name="get_bake_mesh_instance"> - <return type="RID"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="RID" /> + <argument index="0" name="idx" type="int" /> <description> </description> </method> <method name="get_bake_meshes"> - <return type="Array"> - </return> + <return type="Array" /> <description> Returns an array of [ArrayMesh]es and [Transform3D] references of all bake meshes that exist within the current GridMap. </description> </method> <method name="get_cell_item" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="position" type="Vector3i"> - </argument> + <return type="int" /> + <argument index="0" name="position" type="Vector3i" /> <description> The [MeshLibrary] item index located at the given grid coordinates. If the cell is empty, [constant INVALID_CELL_ITEM] will be returned. </description> </method> <method name="get_cell_item_orientation" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="position" type="Vector3i"> - </argument> + <return type="int" /> + <argument index="0" name="position" type="Vector3i" /> <description> The orientation of the cell at the given grid coordinates. [code]-1[/code] is returned if the cell is empty. </description> </method> <method name="get_collision_layer_bit" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="bit" type="int"> - </argument> + <return type="bool" /> + <argument index="0" name="bit" type="int" /> <description> Returns an individual bit on the [member collision_layer]. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="bit" type="int"> - </argument> + <return type="bool" /> + <argument index="0" name="bit" type="int" /> <description> Returns an individual bit on the [member collision_mask]. </description> </method> <method name="get_meshes"> - <return type="Array"> - </return> + <return type="Array" /> <description> Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space. </description> </method> <method name="get_used_cells" qualifiers="const"> - <return type="Array"> - </return> + <return type="Array" /> <description> Returns an array of [Vector3] with the non-empty cell coordinates in the grid map. </description> </method> <method name="make_baked_meshes"> - <return type="void"> - </return> - <argument index="0" name="gen_lightmap_uv" type="bool" default="false"> - </argument> - <argument index="1" name="lightmap_uv_texel_size" type="float" default="0.1"> - </argument> + <return type="void" /> + <argument index="0" name="gen_lightmap_uv" type="bool" default="false" /> + <argument index="1" name="lightmap_uv_texel_size" type="float" default="0.1" /> <description> </description> </method> <method name="map_to_world" qualifiers="const"> - <return type="Vector3"> - </return> - <argument index="0" name="map_position" type="Vector3i"> - </argument> + <return type="Vector3" /> + <argument index="0" name="map_position" type="Vector3i" /> <description> Returns the position of a grid cell in the GridMap's local coordinate space. </description> </method> <method name="resource_changed"> - <return type="void"> - </return> - <argument index="0" name="resource" type="Resource"> - </argument> + <return type="void" /> + <argument index="0" name="resource" type="Resource" /> <description> </description> </method> <method name="set_cell_item"> - <return type="void"> - </return> - <argument index="0" name="position" type="Vector3i"> - </argument> - <argument index="1" name="item" type="int"> - </argument> - <argument index="2" name="orientation" type="int" default="0"> - </argument> + <return type="void" /> + <argument index="0" name="position" type="Vector3i" /> + <argument index="1" name="item" type="int" /> + <argument index="2" name="orientation" type="int" default="0" /> <description> Sets the mesh index for the cell referenced by its grid coordinates. A negative item index such as [constant INVALID_CELL_ITEM] will clear the cell. @@ -137,46 +111,33 @@ </description> </method> <method name="set_clip"> - <return type="void"> - </return> - <argument index="0" name="enabled" type="bool"> - </argument> - <argument index="1" name="clipabove" type="bool" default="true"> - </argument> - <argument index="2" name="floor" type="int" default="0"> - </argument> - <argument index="3" name="axis" type="int" enum="Vector3.Axis" default="0"> - </argument> + <return type="void" /> + <argument index="0" name="enabled" type="bool" /> + <argument index="1" name="clipabove" type="bool" default="true" /> + <argument index="2" name="floor" type="int" default="0" /> + <argument index="3" name="axis" type="int" enum="Vector3.Axis" default="0" /> <description> </description> </method> <method name="set_collision_layer_bit"> - <return type="void"> - </return> - <argument index="0" name="bit" type="int"> - </argument> - <argument index="1" name="value" type="bool"> - </argument> + <return type="void" /> + <argument index="0" name="bit" type="int" /> + <argument index="1" name="value" type="bool" /> <description> Sets an individual bit on the [member collision_layer]. </description> </method> <method name="set_collision_mask_bit"> - <return type="void"> - </return> - <argument index="0" name="bit" type="int"> - </argument> - <argument index="1" name="value" type="bool"> - </argument> + <return type="void" /> + <argument index="0" name="bit" type="int" /> + <argument index="1" name="value" type="bool" /> <description> Sets an individual bit on the [member collision_mask]. </description> </method> <method name="world_to_map" qualifiers="const"> - <return type="Vector3i"> - </return> - <argument index="0" name="world_position" type="Vector3"> - </argument> + <return type="Vector3i" /> + <argument index="0" name="world_position" type="Vector3" /> <description> Returns the coordinates of the grid cell containing the given point. [code]pos[/code] should be in the GridMap's local coordinate space. @@ -223,8 +184,7 @@ </members> <signals> <signal name="cell_size_changed"> - <argument index="0" name="cell_size" type="Vector3"> - </argument> + <argument index="0" name="cell_size" type="Vector3" /> <description> Emitted when [member cell_size] changes. </description> diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 989c2d295c..f101c43e89 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -675,7 +675,7 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In } if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_SELECT) { - undo_redo->create_action("GridMap Selection"); + undo_redo->create_action(TTR("GridMap Selection")); undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end); undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end); undo_redo->commit_action(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs index 061c572c32..1a3b81487f 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs @@ -670,20 +670,12 @@ namespace Godot public override string ToString() { - return String.Format("{0}, {1}", new object[] - { - _position.ToString(), - _size.ToString() - }); + return $"{_position}, {_size}"; } public string ToString(string format) { - return String.Format("{0}, {1}", new object[] - { - _position.ToString(format), - _size.ToString(format) - }); + return $"{_position.ToString(format)}, {_size.ToString(format)}"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index cef343e152..8271b43b48 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -863,16 +863,12 @@ namespace Godot public override string ToString() { - return "[X: " + x.ToString() + - ", Y: " + y.ToString() + - ", Z: " + z.ToString() + "]"; + return $"[X: {x}, Y: {y}, Z: {z}]"; } public string ToString(string format) { - return "[X: " + x.ToString(format) + - ", Y: " + y.ToString(format) + - ", Z: " + z.ToString(format) + "]"; + return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, Z: {z.ToString(format)}]"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index 155ffcff32..b9a98ba9c7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -1010,12 +1010,12 @@ namespace Godot public override string ToString() { - return String.Format("({0}, {1}, {2}, {3})", r.ToString(), g.ToString(), b.ToString(), a.ToString()); + return $"({r}, {g}, {b}, {a})"; } public string ToString(string format) { - return String.Format("({0}, {1}, {2}, {3})", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format)); + return $"({r.ToString(format)}, {g.ToString(format)}, {b.ToString(format)}, {a.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index e7d4c40034..f8f5e27397 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -28,16 +28,6 @@ namespace Godot return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); } - public static real_t DecTime(real_t value, real_t amount, real_t step) - { - real_t sgn = Mathf.Sign(value); - real_t val = Mathf.Abs(value); - val -= amount * step; - if (val < 0) - val = 0; - return val * sgn; - } - public static int Hash(object var) { return godot_icall_GD_hash(var); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 1278fb53c2..6972102730 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -355,20 +355,12 @@ namespace Godot public override string ToString() { - return String.Format("{0}, {1}", new object[] - { - _normal.ToString(), - D.ToString() - }); + return $"{_normal}, {D}"; } public string ToString(string format) { - return String.Format("{0}, {1}", new object[] - { - _normal.ToString(format), - D.ToString(format) - }); + return $"{_normal.ToString(format)}, {D.ToString(format)}"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index 35f8217432..0fed55cc30 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -547,12 +547,12 @@ namespace Godot public override string ToString() { - return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString()); + return $"({x}, {y}, {z}, {w})"; } public string ToString(string format) { - return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format)); + return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs index 1053baaa26..dec69c7f94 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -405,20 +405,12 @@ namespace Godot public override string ToString() { - return String.Format("{0}, {1}", new object[] - { - _position.ToString(), - _size.ToString() - }); + return $"{_position}, {_size}"; } public string ToString(string format) { - return String.Format("{0}, {1}", new object[] - { - _position.ToString(format), - _size.ToString(format) - }); + return $"{_position.ToString(format)}, {_size.ToString(format)}"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs index c27af74866..7fb6614d2c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs @@ -383,20 +383,12 @@ namespace Godot public override string ToString() { - return String.Format("{0}, {1}", new object[] - { - _position.ToString(), - _size.ToString() - }); + return $"{_position}, {_size}"; } public string ToString(string format) { - return String.Format("{0}, {1}", new object[] - { - _position.ToString(format), - _size.ToString(format) - }); + return $"{_position.ToString(format)}, {_size.ToString(format)}"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs index b182f1f15e..d9665cbf2b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -1108,7 +1108,7 @@ namespace Godot /// </summary> public static string[] Split(this string instance, string divisor, bool allowEmpty = true) { - return instance.Split(new[] { divisor }, StringSplitOptions.RemoveEmptyEntries); + return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index c135eb137f..62a6fe6959 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -492,16 +492,12 @@ namespace Godot public override string ToString() { - return "[X: " + x.ToString() + - ", Y: " + y.ToString() + - ", O: " + origin.ToString() + "]"; + return $"[X: {x}, Y: {y}, O: {origin}]"; } public string ToString(string format) { - return "[X: " + x.ToString(format) + - ", Y: " + y.ToString(format) + - ", O: " + origin.ToString(format) + "]"; + return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, O: {origin.ToString(format)}]"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 5c57203ca7..1b717fb4ae 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -393,18 +393,12 @@ namespace Godot public override string ToString() { - return "[X: " + basis.x.ToString() + - ", Y: " + basis.y.ToString() + - ", Z: " + basis.z.ToString() + - ", O: " + origin.ToString() + "]"; + return $"[X: {basis.x}, Y: {basis.y}, Z: {basis.z}, O: {origin}]"; } public string ToString(string format) { - return "[X: " + basis.x.ToString(format) + - ", Y: " + basis.y.ToString(format) + - ", Z: " + basis.z.ToString(format) + - ", O: " + origin.ToString(format) + "]"; + return $"[X: {basis.x.ToString(format)}, Y: {basis.y.ToString(format)}, Z: {basis.z.ToString(format)}, O: {origin.ToString(format)}]"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index a0948d8b10..8bb5e90a68 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -751,20 +751,12 @@ namespace Godot public override string ToString() { - return String.Format("({0}, {1})", new object[] - { - x.ToString(), - y.ToString() - }); + return $"({x}, {y})"; } public string ToString(string format) { - return String.Format("({0}, {1})", new object[] - { - x.ToString(format), - y.ToString(format) - }); + return $"({x.ToString(format)}, {y.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs index b4c631608e..959f262f52 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs @@ -508,20 +508,12 @@ namespace Godot public override string ToString() { - return String.Format("({0}, {1})", new object[] - { - this.x.ToString(), - this.y.ToString() - }); + return $"({x}, {y})"; } public string ToString(string format) { - return String.Format("({0}, {1})", new object[] - { - this.x.ToString(format), - this.y.ToString(format) - }); + return $"({x.ToString(format)}, {y.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index a02a0d2dd9..bdf64159e9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -845,22 +845,12 @@ namespace Godot public override string ToString() { - return String.Format("({0}, {1}, {2})", new object[] - { - x.ToString(), - y.ToString(), - z.ToString() - }); + return $"({x}, {y}, {z})"; } public string ToString(string format) { - return String.Format("({0}, {1}, {2})", new object[] - { - x.ToString(format), - y.ToString(format), - z.ToString(format) - }); + return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})"; } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs index 80bad061b2..c96a7cf1b0 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs @@ -511,22 +511,12 @@ namespace Godot public override string ToString() { - return String.Format("({0}, {1}, {2})", new object[] - { - this.x.ToString(), - this.y.ToString(), - this.z.ToString() - }); + return $"({x}, {y}, {z})"; } public string ToString(string format) { - return String.Format("({0}, {1}, {2})", new object[] - { - this.x.ToString(format), - this.y.ToString(format), - this.z.ToString(format) - }); + return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})"; } } } diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index 4d45e41cc3..c470f3e1ab 100644 --- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -25,90 +25,66 @@ </tutorials> <methods> <method name="get_image" qualifiers="const"> - <return type="Image"> - </return> - <argument index="0" name="width" type="int"> - </argument> - <argument index="1" name="height" type="int"> - </argument> - <argument index="2" name="noise_offset" type="Vector2" default="Vector2(0, 0)"> - </argument> + <return type="Image" /> + <argument index="0" name="width" type="int" /> + <argument index="1" name="height" type="int" /> + <argument index="2" name="noise_offset" type="Vector2" default="Vector2(0, 0)" /> <description> Generate a noise image in [constant Image.FORMAT_L8] format with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. If [code]noise_offset[/code] is specified, then the offset value is used as the coordinates of the top-left corner of the generated noise. </description> </method> <method name="get_noise_1d" qualifiers="const"> - <return type="float"> - </return> - <argument index="0" name="x" type="float"> - </argument> + <return type="float" /> + <argument index="0" name="x" type="float" /> <description> Returns the 1D noise value [code][-1,1][/code] at the given x-coordinate. [b]Note:[/b] 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" qualifiers="const"> - <return type="float"> - </return> - <argument index="0" name="x" type="float"> - </argument> - <argument index="1" name="y" type="float"> - </argument> + <return type="float" /> + <argument index="0" name="x" type="float" /> + <argument index="1" name="y" type="float" /> <description> Returns the 2D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_noise_2dv" qualifiers="const"> - <return type="float"> - </return> - <argument index="0" name="pos" type="Vector2"> - </argument> + <return type="float" /> + <argument index="0" name="pos" type="Vector2" /> <description> Returns the 2D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_noise_3d" qualifiers="const"> - <return type="float"> - </return> - <argument index="0" name="x" type="float"> - </argument> - <argument index="1" name="y" type="float"> - </argument> - <argument index="2" name="z" type="float"> - </argument> + <return type="float" /> + <argument index="0" name="x" type="float" /> + <argument index="1" name="y" type="float" /> + <argument index="2" name="z" type="float" /> <description> Returns the 3D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_noise_3dv" qualifiers="const"> - <return type="float"> - </return> - <argument index="0" name="pos" type="Vector3"> - </argument> + <return type="float" /> + <argument index="0" name="pos" type="Vector3" /> <description> Returns the 3D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_noise_4d" qualifiers="const"> - <return type="float"> - </return> - <argument index="0" name="x" type="float"> - </argument> - <argument index="1" name="y" type="float"> - </argument> - <argument index="2" name="z" type="float"> - </argument> - <argument index="3" name="w" type="float"> - </argument> + <return type="float" /> + <argument index="0" name="x" type="float" /> + <argument index="1" name="y" type="float" /> + <argument index="2" name="z" type="float" /> + <argument index="3" name="w" type="float" /> <description> Returns the 4D noise value [code][-1,1][/code] at the given position. </description> </method> <method name="get_seamless_image" qualifiers="const"> - <return type="Image"> - </return> - <argument index="0" name="size" type="int"> - </argument> + <return type="Image" /> + <argument index="0" name="size" type="int" /> <description> Generate a tileable noise image in [constant Image.FORMAT_L8] format, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] × [code]size[/code]). [b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise. diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 7f5a0dfecb..9c84974ff6 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -50,88 +50,67 @@ </tutorials> <methods> <method name="clear"> - <return type="void"> - </return> + <return type="void" /> <description> This method resets the state of the object, as if it was freshly created. Namely, it unassigns the regular expression of this object. </description> </method> <method name="compile"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="pattern" type="String"> - </argument> + <return type="int" enum="Error" /> + <argument index="0" name="pattern" type="String" /> <description> Compiles and assign the search pattern to use. Returns [constant OK] if the compilation is successful. If an error is encountered, details are printed to standard output and an error is returned. </description> </method> <method name="get_group_count" qualifiers="const"> - <return type="int"> - </return> + <return type="int" /> <description> Returns the number of capturing groups in compiled pattern. </description> </method> <method name="get_names" qualifiers="const"> - <return type="Array"> - </return> + <return type="Array" /> <description> Returns an array of names of named capturing groups in the compiled pattern. They are ordered by appearance. </description> </method> <method name="get_pattern" qualifiers="const"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the original search pattern that was compiled. </description> </method> <method name="is_valid" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <description> Returns whether this object has a valid search pattern assigned. </description> </method> <method name="search" qualifiers="const"> - <return type="RegExMatch"> - </return> - <argument index="0" name="subject" type="String"> - </argument> - <argument index="1" name="offset" type="int" default="0"> - </argument> - <argument index="2" name="end" type="int" default="-1"> - </argument> + <return type="RegExMatch" /> + <argument index="0" name="subject" type="String" /> + <argument index="1" name="offset" type="int" default="0" /> + <argument index="2" name="end" type="int" default="-1" /> <description> Searches the text for the compiled pattern. Returns a [RegExMatch] container of the first matching result if found, otherwise [code]null[/code]. The region to search within can be specified without modifying where the start and end anchor would be. </description> </method> <method name="search_all" qualifiers="const"> - <return type="Array"> - </return> - <argument index="0" name="subject" type="String"> - </argument> - <argument index="1" name="offset" type="int" default="0"> - </argument> - <argument index="2" name="end" type="int" default="-1"> - </argument> + <return type="Array" /> + <argument index="0" name="subject" type="String" /> + <argument index="1" name="offset" type="int" default="0" /> + <argument index="2" name="end" type="int" default="-1" /> <description> Searches the text for the compiled pattern. Returns an array of [RegExMatch] containers for each non-overlapping result. If no results were found, an empty array is returned instead. The region to search within can be specified without modifying where the start and end anchor would be. </description> </method> <method name="sub" qualifiers="const"> - <return type="String"> - </return> - <argument index="0" name="subject" type="String"> - </argument> - <argument index="1" name="replacement" type="String"> - </argument> - <argument index="2" name="all" type="bool" default="false"> - </argument> - <argument index="3" name="offset" type="int" default="0"> - </argument> - <argument index="4" name="end" type="int" default="-1"> - </argument> + <return type="String" /> + <argument index="0" name="subject" type="String" /> + <argument index="1" name="replacement" type="String" /> + <argument index="2" name="all" type="bool" default="false" /> + <argument index="3" name="offset" type="int" default="0" /> + <argument index="4" name="end" type="int" default="-1" /> <description> Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]$1[/code] and [code]$name[/code] are expanded and resolved. By default, only the first instance is replaced, but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be. </description> diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index 492519d3d9..3cde2836cc 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -10,37 +10,30 @@ </tutorials> <methods> <method name="get_end" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="name" type="Variant" default="0"> - </argument> + <return type="int" /> + <argument index="0" name="name" type="Variant" default="0" /> <description> Returns the end position of the match within the source string. The end position of capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern. Returns -1 if the group did not match or doesn't exist. </description> </method> <method name="get_group_count" qualifiers="const"> - <return type="int"> - </return> + <return type="int" /> <description> Returns the number of capturing groups. </description> </method> <method name="get_start" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="name" type="Variant" default="0"> - </argument> + <return type="int" /> + <argument index="0" name="name" type="Variant" default="0" /> <description> Returns the starting position of the match within the source string. The starting position of capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern. Returns -1 if the group did not match or doesn't exist. </description> </method> <method name="get_string" qualifiers="const"> - <return type="String"> - </return> - <argument index="0" name="name" type="Variant" default="0"> - </argument> + <return type="String" /> + <argument index="0" name="name" type="Variant" default="0" /> <description> Returns the substring of the match from the source string. Capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern. Returns an empty string if the group did not match or doesn't exist. diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index cb8852d5ef..e7bf9b202d 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -11,17 +11,14 @@ </tutorials> <methods> <method name="get_file"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the Ogg Theora video file handled by this [VideoStreamTheora]. </description> </method> <method name="set_file"> - <return type="void"> - </return> - <argument index="0" name="file" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="file" type="String" /> <description> Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension. </description> diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml index 09a2c8a88c..5b1d9dbfd1 100644 --- a/modules/upnp/doc_classes/UPNP.xml +++ b/modules/upnp/doc_classes/UPNP.xml @@ -21,27 +21,19 @@ </tutorials> <methods> <method name="add_device"> - <return type="void"> - </return> - <argument index="0" name="device" type="UPNPDevice"> - </argument> + <return type="void" /> + <argument index="0" name="device" type="UPNPDevice" /> <description> Adds the given [UPNPDevice] to the list of discovered devices. </description> </method> <method name="add_port_mapping" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="port" type="int"> - </argument> - <argument index="1" name="port_internal" type="int" default="0"> - </argument> - <argument index="2" name="desc" type="String" default=""""> - </argument> - <argument index="3" name="proto" type="String" default=""UDP""> - </argument> - <argument index="4" name="duration" type="int" default="0"> - </argument> + <return type="int" /> + <argument index="0" name="port" type="int" /> + <argument index="1" name="port_internal" type="int" default="0" /> + <argument index="2" name="desc" type="String" default="""" /> + <argument index="3" name="proto" type="String" default=""UDP"" /> + <argument index="4" name="duration" type="int" default="0" /> <description> Adds a mapping to forward the external [code]port[/code] (between 1 and 65535) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any. If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value). @@ -50,32 +42,24 @@ </description> </method> <method name="clear_devices"> - <return type="void"> - </return> + <return type="void" /> <description> Clears the list of discovered devices. </description> </method> <method name="delete_port_mapping" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="port" type="int"> - </argument> - <argument index="1" name="proto" type="String" default=""UDP""> - </argument> + <return type="int" /> + <argument index="0" name="port" type="int" /> + <argument index="1" name="proto" type="String" default=""UDP"" /> <description> Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. See [enum UPNPResult] for possible return values. </description> </method> <method name="discover"> - <return type="int"> - </return> - <argument index="0" name="timeout" type="int" default="2000"> - </argument> - <argument index="1" name="ttl" type="int" default="2"> - </argument> - <argument index="2" name="device_filter" type="String" default=""InternetGatewayDevice""> - </argument> + <return type="int" /> + <argument index="0" name="timeout" type="int" default="2000" /> + <argument index="1" name="ttl" type="int" default="2" /> + <argument index="2" name="device_filter" type="String" default=""InternetGatewayDevice"" /> <description> Discovers local [UPNPDevice]s. Clears the list of previously discovered devices. Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in milliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing. @@ -83,51 +67,41 @@ </description> </method> <method name="get_device" qualifiers="const"> - <return type="UPNPDevice"> - </return> - <argument index="0" name="index" type="int"> - </argument> + <return type="UPNPDevice" /> + <argument index="0" name="index" type="int" /> <description> Returns the [UPNPDevice] at the given [code]index[/code]. </description> </method> <method name="get_device_count" qualifiers="const"> - <return type="int"> - </return> + <return type="int" /> <description> Returns the number of discovered [UPNPDevice]s. </description> </method> <method name="get_gateway" qualifiers="const"> - <return type="UPNPDevice"> - </return> + <return type="UPNPDevice" /> <description> Returns the default gateway. That is the first discovered [UPNPDevice] that is also a valid IGD (InternetGatewayDevice). </description> </method> <method name="query_external_address" qualifiers="const"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the external [IP] address of the default gateway (see [method get_gateway]) as string. Returns an empty string on error. </description> </method> <method name="remove_device"> - <return type="void"> - </return> - <argument index="0" name="index" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="index" type="int" /> <description> Removes the device at [code]index[/code] from the list of discovered devices. </description> </method> <method name="set_device"> - <return type="void"> - </return> - <argument index="0" name="index" type="int"> - </argument> - <argument index="1" name="device" type="UPNPDevice"> - </argument> + <return type="void" /> + <argument index="0" name="index" type="int" /> + <argument index="1" name="device" type="UPNPDevice" /> <description> Sets the device at [code]index[/code] from the list of discovered devices to [code]device[/code]. </description> diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml index f7b5386d86..b7c2ff7dd7 100644 --- a/modules/upnp/doc_classes/UPNPDevice.xml +++ b/modules/upnp/doc_classes/UPNPDevice.xml @@ -10,43 +10,32 @@ </tutorials> <methods> <method name="add_port_mapping" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="port" type="int"> - </argument> - <argument index="1" name="port_internal" type="int" default="0"> - </argument> - <argument index="2" name="desc" type="String" default=""""> - </argument> - <argument index="3" name="proto" type="String" default=""UDP""> - </argument> - <argument index="4" name="duration" type="int" default="0"> - </argument> + <return type="int" /> + <argument index="0" name="port" type="int" /> + <argument index="1" name="port_internal" type="int" default="0" /> + <argument index="2" name="desc" type="String" default="""" /> + <argument index="3" name="proto" type="String" default=""UDP"" /> + <argument index="4" name="duration" type="int" default="0" /> <description> Adds a port mapping to forward the given external port on this [UPNPDevice] for the given protocol to the local machine. See [method UPNP.add_port_mapping]. </description> </method> <method name="delete_port_mapping" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="port" type="int"> - </argument> - <argument index="1" name="proto" type="String" default=""UDP""> - </argument> + <return type="int" /> + <argument index="0" name="port" type="int" /> + <argument index="1" name="proto" type="String" default=""UDP"" /> <description> Deletes the port mapping identified by the given port and protocol combination on this device. See [method UPNP.delete_port_mapping]. </description> </method> <method name="is_valid_gateway" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <description> Returns [code]true[/code] if this is a valid IGD (InternetGatewayDevice) which potentially supports port forwarding. </description> </method> <method name="query_external_address" qualifiers="const"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the external IP address of this [UPNPDevice] or an empty string. </description> diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index 9d51bd86a2..2327fc0009 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -13,456 +13,334 @@ </tutorials> <methods> <method name="add_custom_signal"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> <description> Add a custom signal with the specified name to the VisualScript. </description> </method> <method name="add_function"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="func_node_id" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="func_node_id" type="int" /> <description> Add a function with the specified name to the VisualScript, and assign the root [VisualScriptFunction] node's id as [code]func_node_id[/code]. </description> </method> <method name="add_node"> - <return type="void"> - </return> - <argument index="0" name="id" type="int"> - </argument> - <argument index="1" name="node" type="VisualScriptNode"> - </argument> - <argument index="2" name="position" type="Vector2" default="Vector2(0, 0)"> - </argument> + <return type="void" /> + <argument index="0" name="id" type="int" /> + <argument index="1" name="node" type="VisualScriptNode" /> + <argument index="2" name="position" type="Vector2" default="Vector2(0, 0)" /> <description> Add a node to the VisualScript. </description> </method> <method name="add_variable"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="default_value" type="Variant" default="null"> - </argument> - <argument index="2" name="export" type="bool" default="false"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="default_value" type="Variant" default="null" /> + <argument index="2" name="export" type="bool" default="false" /> <description> Add a variable to the VisualScript, optionally giving it a default value or marking it as exported. </description> </method> <method name="custom_signal_add_argument"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="type" type="int" enum="Variant.Type"> - </argument> - <argument index="2" name="argname" type="String"> - </argument> - <argument index="3" name="index" type="int" default="-1"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="type" type="int" enum="Variant.Type" /> + <argument index="2" name="argname" type="String" /> + <argument index="3" name="index" type="int" default="-1" /> <description> Add an argument to a custom signal added with [method add_custom_signal]. </description> </method> <method name="custom_signal_get_argument_count" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="int" /> + <argument index="0" name="name" type="StringName" /> <description> Get the count of a custom signal's arguments. </description> </method> <method name="custom_signal_get_argument_name" qualifiers="const"> - <return type="String"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="argidx" type="int"> - </argument> + <return type="String" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="argidx" type="int" /> <description> Get the name of a custom signal's argument. </description> </method> <method name="custom_signal_get_argument_type" qualifiers="const"> - <return type="int" enum="Variant.Type"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="argidx" type="int"> - </argument> + <return type="int" enum="Variant.Type" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="argidx" type="int" /> <description> Get the type of a custom signal's argument. </description> </method> <method name="custom_signal_remove_argument"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="argidx" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="argidx" type="int" /> <description> Remove a specific custom signal's argument. </description> </method> <method name="custom_signal_set_argument_name"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="argidx" type="int"> - </argument> - <argument index="2" name="argname" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="argidx" type="int" /> + <argument index="2" name="argname" type="String" /> <description> Rename a custom signal's argument. </description> </method> <method name="custom_signal_set_argument_type"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="argidx" type="int"> - </argument> - <argument index="2" name="type" type="int" enum="Variant.Type"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="argidx" type="int" /> + <argument index="2" name="type" type="int" enum="Variant.Type" /> <description> Change the type of a custom signal's argument. </description> </method> <method name="custom_signal_swap_argument"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="argidx" type="int"> - </argument> - <argument index="2" name="withidx" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="argidx" type="int" /> + <argument index="2" name="withidx" type="int" /> <description> Swap two of the arguments of a custom signal. </description> </method> <method name="data_connect"> - <return type="void"> - </return> - <argument index="0" name="from_node" type="int"> - </argument> - <argument index="1" name="from_port" type="int"> - </argument> - <argument index="2" name="to_node" type="int"> - </argument> - <argument index="3" name="to_port" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="from_node" type="int" /> + <argument index="1" name="from_port" type="int" /> + <argument index="2" name="to_node" type="int" /> + <argument index="3" name="to_port" type="int" /> <description> Connect two data ports. The value of [code]from_node[/code]'s [code]from_port[/code] would be fed into [code]to_node[/code]'s [code]to_port[/code]. </description> </method> <method name="data_disconnect"> - <return type="void"> - </return> - <argument index="0" name="from_node" type="int"> - </argument> - <argument index="1" name="from_port" type="int"> - </argument> - <argument index="2" name="to_node" type="int"> - </argument> - <argument index="3" name="to_port" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="from_node" type="int" /> + <argument index="1" name="from_port" type="int" /> + <argument index="2" name="to_node" type="int" /> + <argument index="3" name="to_port" type="int" /> <description> Disconnect two data ports previously connected with [method data_connect]. </description> </method> <method name="get_function_node_id" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="int" /> + <argument index="0" name="name" type="StringName" /> <description> Returns the id of a function's entry point node. </description> </method> <method name="get_node" qualifiers="const"> - <return type="VisualScriptNode"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <return type="VisualScriptNode" /> + <argument index="0" name="id" type="int" /> <description> Returns a node given its id. </description> </method> <method name="get_node_position" qualifiers="const"> - <return type="Vector2"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <return type="Vector2" /> + <argument index="0" name="id" type="int" /> <description> Returns a node's position in pixels. </description> </method> <method name="get_scroll" qualifiers="const"> - <return type="Vector2"> - </return> + <return type="Vector2" /> <description> Returns the current position of the center of the screen. </description> </method> <method name="get_variable_default_value" qualifiers="const"> - <return type="Variant"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="Variant" /> + <argument index="0" name="name" type="StringName" /> <description> Returns the default (initial) value of a variable. </description> </method> <method name="get_variable_export" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="bool" /> + <argument index="0" name="name" type="StringName" /> <description> Returns whether a variable is exported. </description> </method> <method name="get_variable_info" qualifiers="const"> - <return type="Dictionary"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="Dictionary" /> + <argument index="0" name="name" type="StringName" /> <description> Returns the information for a given variable as a dictionary. The information includes its name, type, hint and usage. </description> </method> <method name="has_custom_signal" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="bool" /> + <argument index="0" name="name" type="StringName" /> <description> Returns whether a signal exists with the specified name. </description> </method> <method name="has_data_connection" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="from_node" type="int"> - </argument> - <argument index="1" name="from_port" type="int"> - </argument> - <argument index="2" name="to_node" type="int"> - </argument> - <argument index="3" name="to_port" type="int"> - </argument> + <return type="bool" /> + <argument index="0" name="from_node" type="int" /> + <argument index="1" name="from_port" type="int" /> + <argument index="2" name="to_node" type="int" /> + <argument index="3" name="to_port" type="int" /> <description> Returns whether the specified data ports are connected. </description> </method> <method name="has_function" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="bool" /> + <argument index="0" name="name" type="StringName" /> <description> Returns whether a function exists with the specified name. </description> </method> <method name="has_node" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <return type="bool" /> + <argument index="0" name="id" type="int" /> <description> Returns whether a node exists with the given id. </description> </method> <method name="has_sequence_connection" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="from_node" type="int"> - </argument> - <argument index="1" name="from_output" type="int"> - </argument> - <argument index="2" name="to_node" type="int"> - </argument> + <return type="bool" /> + <argument index="0" name="from_node" type="int" /> + <argument index="1" name="from_output" type="int" /> + <argument index="2" name="to_node" type="int" /> <description> Returns whether the specified sequence ports are connected. </description> </method> <method name="has_variable" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="bool" /> + <argument index="0" name="name" type="StringName" /> <description> Returns whether a variable exists with the specified name. </description> </method> <method name="remove_custom_signal"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> <description> Remove a custom signal with the given name. </description> </method> <method name="remove_function"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> <description> Remove a specific function and its nodes from the script. </description> </method> <method name="remove_node"> - <return type="void"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="id" type="int" /> <description> Remove the node with the specified id. </description> </method> <method name="remove_variable"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> <description> Remove a variable with the given name. </description> </method> <method name="rename_custom_signal"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="new_name" type="StringName"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="new_name" type="StringName" /> <description> Change the name of a custom signal. </description> </method> <method name="rename_function"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="new_name" type="StringName"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="new_name" type="StringName" /> <description> Change the name of a function. </description> </method> <method name="rename_variable"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="new_name" type="StringName"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="new_name" type="StringName" /> <description> Change the name of a variable. </description> </method> <method name="sequence_connect"> - <return type="void"> - </return> - <argument index="0" name="from_node" type="int"> - </argument> - <argument index="1" name="from_output" type="int"> - </argument> - <argument index="2" name="to_node" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="from_node" type="int" /> + <argument index="1" name="from_output" type="int" /> + <argument index="2" name="to_node" type="int" /> <description> Connect two sequence ports. The execution will flow from of [code]from_node[/code]'s [code]from_output[/code] into [code]to_node[/code]. Unlike [method data_connect], there isn't a [code]to_port[/code], since the target node can have only one sequence port. </description> </method> <method name="sequence_disconnect"> - <return type="void"> - </return> - <argument index="0" name="from_node" type="int"> - </argument> - <argument index="1" name="from_output" type="int"> - </argument> - <argument index="2" name="to_node" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="from_node" type="int" /> + <argument index="1" name="from_output" type="int" /> + <argument index="2" name="to_node" type="int" /> <description> Disconnect two sequence ports previously connected with [method sequence_connect]. </description> </method> <method name="set_instance_base_type"> - <return type="void"> - </return> - <argument index="0" name="type" type="StringName"> - </argument> + <return type="void" /> + <argument index="0" name="type" type="StringName" /> <description> Set the base type of the script. </description> </method> <method name="set_node_position"> - <return type="void"> - </return> - <argument index="0" name="id" type="int"> - </argument> - <argument index="1" name="position" type="Vector2"> - </argument> + <return type="void" /> + <argument index="0" name="id" type="int" /> + <argument index="1" name="position" type="Vector2" /> <description> Set the node position in the VisualScript graph. </description> </method> <method name="set_scroll"> - <return type="void"> - </return> - <argument index="0" name="ofs" type="Vector2"> - </argument> + <return type="void" /> + <argument index="0" name="ofs" type="Vector2" /> <description> Set the screen center to the given position. </description> </method> <method name="set_variable_default_value"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="value" type="Variant"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="value" type="Variant" /> <description> Change the default (initial) value of a variable. </description> </method> <method name="set_variable_export"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="enable" type="bool"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="enable" type="bool" /> <description> Change whether a variable is exported. </description> </method> <method name="set_variable_info"> - <return type="void"> - </return> - <argument index="0" name="name" type="StringName"> - </argument> - <argument index="1" name="value" type="Dictionary"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="StringName" /> + <argument index="1" name="value" type="Dictionary" /> <description> Set a variable's info, using the same format as [method get_variable_info]. </description> @@ -470,8 +348,7 @@ </methods> <signals> <signal name="node_ports_changed"> - <argument index="0" name="id" type="int"> - </argument> + <argument index="0" name="id" type="int" /> <description> Emitted when the ports of a node are changed. </description> diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 219ffd01d3..195d766c1d 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -105,117 +105,114 @@ <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 [code]value[/code] decreased by [code]step[/code] * [code]amount[/code]. - </constant> - <constant name="MATH_RANDOMIZE" value="31" enum="BuiltinFunc"> + <constant name="MATH_RANDOMIZE" value="30" 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_RANDI" value="32" enum="BuiltinFunc"> + <constant name="MATH_RANDI" value="31" 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="33" enum="BuiltinFunc"> + <constant name="MATH_RANDF" value="32" 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_RANDF_RANGE" value="34" enum="BuiltinFunc"> + <constant name="MATH_RANDF_RANGE" value="33" enum="BuiltinFunc"> Return a random floating-point value between the two inputs. </constant> - <constant name="MATH_RANDI_RANGE" value="35" enum="BuiltinFunc"> + <constant name="MATH_RANDI_RANGE" value="34" enum="BuiltinFunc"> Return a random 32-bit integer value between the two inputs. </constant> - <constant name="MATH_SEED" value="36" enum="BuiltinFunc"> + <constant name="MATH_SEED" value="35" enum="BuiltinFunc"> Set the seed for the random number generator. </constant> - <constant name="MATH_RANDSEED" value="37" 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="38" enum="BuiltinFunc"> + <constant name="MATH_DEG2RAD" value="37" enum="BuiltinFunc"> Convert the input from degrees to radians. </constant> - <constant name="MATH_RAD2DEG" value="39" enum="BuiltinFunc"> + <constant name="MATH_RAD2DEG" value="38" enum="BuiltinFunc"> Convert the input from radians to degrees. </constant> - <constant name="MATH_LINEAR2DB" value="40" 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="41" 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="42" 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="43" 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="44" enum="BuiltinFunc"> + <constant name="MATH_WRAP" value="43" enum="BuiltinFunc"> </constant> - <constant name="MATH_WRAPF" value="45" enum="BuiltinFunc"> + <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc"> </constant> - <constant name="LOGIC_MAX" value="46" 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="47" 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="48" 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="49" 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="50" enum="BuiltinFunc"> + <constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc"> Create a [WeakRef] from the input. </constant> - <constant name="TYPE_CONVERT" value="51" enum="BuiltinFunc"> + <constant name="TYPE_CONVERT" value="50" enum="BuiltinFunc"> Convert between types. </constant> - <constant name="TYPE_OF" value="52" enum="BuiltinFunc"> + <constant name="TYPE_OF" value="51" 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="53" enum="BuiltinFunc"> + <constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc"> Checks if a type is registered in the [ClassDB]. </constant> - <constant name="TEXT_CHAR" value="54" enum="BuiltinFunc"> + <constant name="TEXT_CHAR" value="53" enum="BuiltinFunc"> Return a character with the given ascii value. </constant> - <constant name="TEXT_STR" value="55" enum="BuiltinFunc"> + <constant name="TEXT_STR" value="54" enum="BuiltinFunc"> Convert the input to a string. </constant> - <constant name="TEXT_PRINT" value="56" enum="BuiltinFunc"> + <constant name="TEXT_PRINT" value="55" enum="BuiltinFunc"> Print the given string to the output window. </constant> - <constant name="TEXT_PRINTERR" value="57" enum="BuiltinFunc"> + <constant name="TEXT_PRINTERR" value="56" enum="BuiltinFunc"> Print the given string to the standard error output. </constant> - <constant name="TEXT_PRINTRAW" value="58" enum="BuiltinFunc"> + <constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc"> Print the given string to the standard output, without adding a newline. </constant> - <constant name="VAR_TO_STR" value="59" enum="BuiltinFunc"> + <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc"> Serialize a [Variant] to a string. </constant> - <constant name="STR_TO_VAR" value="60" enum="BuiltinFunc"> + <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc"> Deserialize a [Variant] from a string serialized using [constant VAR_TO_STR]. </constant> - <constant name="VAR_TO_BYTES" value="61" enum="BuiltinFunc"> + <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc"> Serialize a [Variant] to a [PackedByteArray]. </constant> - <constant name="BYTES_TO_VAR" value="62" enum="BuiltinFunc"> + <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc"> Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES]. </constant> - <constant name="MATH_SMOOTHSTEP" value="63" enum="BuiltinFunc"> + <constant name="MATH_SMOOTHSTEP" value="62" enum="BuiltinFunc"> Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], 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="MATH_POSMOD" value="64" enum="BuiltinFunc"> + <constant name="MATH_POSMOD" value="63" enum="BuiltinFunc"> </constant> - <constant name="MATH_LERP_ANGLE" value="65" enum="BuiltinFunc"> + <constant name="MATH_LERP_ANGLE" value="64" enum="BuiltinFunc"> </constant> - <constant name="TEXT_ORD" value="66" enum="BuiltinFunc"> + <constant name="TEXT_ORD" value="65" enum="BuiltinFunc"> </constant> - <constant name="FUNC_MAX" value="67" enum="BuiltinFunc"> + <constant name="FUNC_MAX" value="66" enum="BuiltinFunc"> Represents the size of the [enum BuiltinFunc] enum. </constant> </constants> diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml index 2f162e78b6..4743594ec3 100644 --- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml +++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml @@ -10,30 +10,24 @@ </tutorials> <methods> <method name="get_constructor" qualifiers="const"> - <return type="Dictionary"> - </return> + <return type="Dictionary" /> <description> </description> </method> <method name="get_constructor_type" qualifiers="const"> - <return type="int" enum="Variant.Type"> - </return> + <return type="int" enum="Variant.Type" /> <description> </description> </method> <method name="set_constructor"> - <return type="void"> - </return> - <argument index="0" name="constructor" type="Dictionary"> - </argument> + <return type="void" /> + <argument index="0" name="constructor" type="Dictionary" /> <description> </description> </method> <method name="set_constructor_type"> - <return type="void"> - </return> - <argument index="0" name="type" type="int" enum="Variant.Type"> - </argument> + <return type="void" /> + <argument index="0" name="type" type="int" enum="Variant.Type" /> <description> </description> </method> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index ba4eba26fd..8aa34f8cae 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -10,153 +10,122 @@ </tutorials> <methods> <method name="_get_caption" qualifiers="virtual"> - <return type="String"> - </return> + <return type="String" /> <description> Return the node's title. </description> </method> <method name="_get_category" qualifiers="virtual"> - <return type="String"> - </return> + <return type="String" /> <description> Return the node's category. </description> </method> <method name="_get_input_value_port_count" qualifiers="virtual"> - <return type="int"> - </return> + <return type="int" /> <description> Return the count of input value ports. </description> </method> <method name="_get_input_value_port_hint" qualifiers="virtual"> - <return type="int"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="int" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified input port's hint. See the [enum @GlobalScope.PropertyHint] hints. </description> </method> <method name="_get_input_value_port_hint_string" qualifiers="virtual"> - <return type="String"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="String" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified input port's hint string. </description> </method> <method name="_get_input_value_port_name" qualifiers="virtual"> - <return type="String"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="String" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified input port's name. </description> </method> <method name="_get_input_value_port_type" qualifiers="virtual"> - <return type="int"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="int" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified input port's type. See the [enum Variant.Type] values. </description> </method> <method name="_get_output_sequence_port_count" qualifiers="virtual"> - <return type="int"> - </return> + <return type="int" /> <description> Return the amount of output [b]sequence[/b] ports. </description> </method> <method name="_get_output_sequence_port_text" qualifiers="virtual"> - <return type="String"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="String" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified [b]sequence[/b] output's name. </description> </method> <method name="_get_output_value_port_count" qualifiers="virtual"> - <return type="int"> - </return> + <return type="int" /> <description> Return the amount of output value ports. </description> </method> <method name="_get_output_value_port_hint" qualifiers="virtual"> - <return type="int"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="int" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified output port's hint. See the [enum @GlobalScope.PropertyHint] hints. </description> </method> <method name="_get_output_value_port_hint_string" qualifiers="virtual"> - <return type="String"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="String" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified output port's hint string. </description> </method> <method name="_get_output_value_port_name" qualifiers="virtual"> - <return type="String"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="String" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified output port's name. </description> </method> <method name="_get_output_value_port_type" qualifiers="virtual"> - <return type="int"> - </return> - <argument index="0" name="idx" type="int"> - </argument> + <return type="int" /> + <argument index="0" name="idx" type="int" /> <description> Return the specified output port's type. See the [enum Variant.Type] values. </description> </method> <method name="_get_text" qualifiers="virtual"> - <return type="String"> - </return> + <return type="String" /> <description> Return the custom node's text, which is shown right next to the input [b]sequence[/b] port (if there is none, on the place that is usually taken by it). </description> </method> <method name="_get_working_memory_size" qualifiers="virtual"> - <return type="int"> - </return> + <return type="int" /> <description> Return the size of the custom node's working memory. See [method _step] for more details. </description> </method> <method name="_has_input_sequence_port" qualifiers="virtual"> - <return type="bool"> - </return> + <return type="bool" /> <description> Return whether the custom node has an input [b]sequence[/b] port. </description> </method> <method name="_step" qualifiers="virtual"> - <return type="Variant"> - </return> - <argument index="0" name="inputs" type="Array"> - </argument> - <argument index="1" name="outputs" type="Array"> - </argument> - <argument index="2" name="start_mode" type="int"> - </argument> - <argument index="3" name="working_mem" type="Array"> - </argument> + <return type="Variant" /> + <argument index="0" name="inputs" type="Array" /> + <argument index="1" name="outputs" type="Array" /> + <argument index="2" name="start_mode" type="int" /> + <argument index="3" name="working_mem" type="Array" /> <description> Execute the custom node's logic, returning the index of the output sequence port to use or a [String] when there is an error. The [code]inputs[/code] array contains the values of the input ports. diff --git a/modules/visual_script/doc_classes/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptEditor.xml index 186cd21239..9ea889c77b 100644 --- a/modules/visual_script/doc_classes/VisualScriptEditor.xml +++ b/modules/visual_script/doc_classes/VisualScriptEditor.xml @@ -8,25 +8,18 @@ </tutorials> <methods> <method name="add_custom_node"> - <return type="void"> - </return> - <argument index="0" name="name" type="String"> - </argument> - <argument index="1" name="category" type="String"> - </argument> - <argument index="2" name="script" type="Script"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="String" /> + <argument index="1" name="category" type="String" /> + <argument index="2" name="script" type="Script" /> <description> Add a custom Visual Script node to the editor. It'll be placed under "Custom Nodes" with the [code]category[/code] as the parameter. </description> </method> <method name="remove_custom_node"> - <return type="void"> - </return> - <argument index="0" name="name" type="String"> - </argument> - <argument index="1" name="category" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="name" type="String" /> + <argument index="1" name="category" type="String" /> <description> Remove a custom Visual Script node from the editor. Custom nodes already placed on scripts won't be removed. </description> diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml index 54a02bf270..18c3826df8 100644 --- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml +++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml @@ -10,30 +10,23 @@ </tutorials> <methods> <method name="connect_to_signal"> - <return type="void"> - </return> - <argument index="0" name="obj" type="Object"> - </argument> - <argument index="1" name="signals" type="String"> - </argument> - <argument index="2" name="args" type="Array"> - </argument> + <return type="void" /> + <argument index="0" name="obj" type="Object" /> + <argument index="1" name="signals" type="String" /> + <argument index="2" name="args" type="Array" /> <description> Connects this [VisualScriptFunctionState] to a signal in the given object to automatically resume when it's emitted. </description> </method> <method name="is_valid" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <description> Returns whether the function state is valid. </description> </method> <method name="resume"> - <return type="Variant"> - </return> - <argument index="0" name="args" type="Array" default="[]"> - </argument> + <return type="Variant" /> + <argument index="0" name="args" type="Array" default="[]" /> <description> Resumes the function to run from the point it was yielded. </description> diff --git a/modules/visual_script/doc_classes/VisualScriptLists.xml b/modules/visual_script/doc_classes/VisualScriptLists.xml index 671c427228..d5bff1341a 100644 --- a/modules/visual_script/doc_classes/VisualScriptLists.xml +++ b/modules/visual_script/doc_classes/VisualScriptLists.xml @@ -10,89 +10,65 @@ </tutorials> <methods> <method name="add_input_data_port"> - <return type="void"> - </return> - <argument index="0" name="type" type="int" enum="Variant.Type"> - </argument> - <argument index="1" name="name" type="String"> - </argument> - <argument index="2" name="index" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="type" type="int" enum="Variant.Type" /> + <argument index="1" name="name" type="String" /> + <argument index="2" name="index" type="int" /> <description> Adds an input port to the Visual Script node. </description> </method> <method name="add_output_data_port"> - <return type="void"> - </return> - <argument index="0" name="type" type="int" enum="Variant.Type"> - </argument> - <argument index="1" name="name" type="String"> - </argument> - <argument index="2" name="index" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="type" type="int" enum="Variant.Type" /> + <argument index="1" name="name" type="String" /> + <argument index="2" name="index" type="int" /> <description> Adds an output port to the Visual Script node. </description> </method> <method name="remove_input_data_port"> - <return type="void"> - </return> - <argument index="0" name="index" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="index" type="int" /> <description> Removes an input port from the Visual Script node. </description> </method> <method name="remove_output_data_port"> - <return type="void"> - </return> - <argument index="0" name="index" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="index" type="int" /> <description> Removes an output port from the Visual Script node. </description> </method> <method name="set_input_data_port_name"> - <return type="void"> - </return> - <argument index="0" name="index" type="int"> - </argument> - <argument index="1" name="name" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="index" type="int" /> + <argument index="1" name="name" type="String" /> <description> Sets the name of an input port. </description> </method> <method name="set_input_data_port_type"> - <return type="void"> - </return> - <argument index="0" name="index" type="int"> - </argument> - <argument index="1" name="type" type="int" enum="Variant.Type"> - </argument> + <return type="void" /> + <argument index="0" name="index" type="int" /> + <argument index="1" name="type" type="int" enum="Variant.Type" /> <description> Sets the type of an input port. </description> </method> <method name="set_output_data_port_name"> - <return type="void"> - </return> - <argument index="0" name="index" type="int"> - </argument> - <argument index="1" name="name" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="index" type="int" /> + <argument index="1" name="name" type="String" /> <description> Sets the name of an output port. </description> </method> <method name="set_output_data_port_type"> - <return type="void"> - </return> - <argument index="0" name="index" type="int"> - </argument> - <argument index="1" name="type" type="int" enum="Variant.Type"> - </argument> + <return type="void" /> + <argument index="0" name="index" type="int" /> + <argument index="1" name="type" type="int" enum="Variant.Type" /> <description> Sets the type of an output port. </description> diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml index 82a023f3e4..23574a5ea8 100644 --- a/modules/visual_script/doc_classes/VisualScriptNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptNode.xml @@ -10,35 +10,28 @@ </tutorials> <methods> <method name="get_default_input_value" qualifiers="const"> - <return type="Variant"> - </return> - <argument index="0" name="port_idx" type="int"> - </argument> + <return type="Variant" /> + <argument index="0" name="port_idx" type="int" /> <description> Returns the default value of a given port. The default value is used when nothing is connected to the port. </description> </method> <method name="get_visual_script" qualifiers="const"> - <return type="VisualScript"> - </return> + <return type="VisualScript" /> <description> Returns the [VisualScript] instance the node is bound to. </description> </method> <method name="ports_changed_notify"> - <return type="void"> - </return> + <return type="void" /> <description> Notify that the node's ports have changed. Usually used in conjunction with [VisualScriptCustomNode] . </description> </method> <method name="set_default_input_value"> - <return type="void"> - </return> - <argument index="0" name="port_idx" type="int"> - </argument> - <argument index="1" name="value" type="Variant"> - </argument> + <return type="void" /> + <argument index="0" name="port_idx" type="int" /> + <argument index="1" name="value" type="Variant" /> <description> Change the default value of a given port. </description> diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml index 89a10edde4..374e7d0f35 100644 --- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml +++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml @@ -10,10 +10,8 @@ </tutorials> <methods> <method name="_subcall" qualifiers="virtual"> - <return type="Variant"> - </return> - <argument index="0" name="arguments" type="Variant"> - </argument> + <return type="Variant" /> + <argument index="0" name="arguments" type="Variant" /> <description> Called by this node. </description> diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 7a2404fd80..86793af77f 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -271,6 +271,7 @@ void VisualScript::_node_ports_changed(int p_id) { void VisualScript::add_node(int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos) { ERR_FAIL_COND(instances.size()); ERR_FAIL_COND(nodes.has(p_id)); // ID can exist only one in script. + ERR_FAIL_COND(p_node.is_null()); NodeData nd; nd.node = p_node; diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index f17ad62531..c61c3ae272 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -68,7 +68,6 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "inverse_lerp", "range_lerp", "move_toward", - "dectime", "randomize", "randi", "randf", @@ -206,7 +205,6 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case MATH_INVERSE_LERP: case MATH_SMOOTHSTEP: case MATH_MOVE_TOWARD: - case MATH_DECTIME: case MATH_WRAP: case MATH_WRAPF: case LOGIC_CLAMP: @@ -349,15 +347,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const return PropertyInfo(Variant::FLOAT, "delta"); } } break; - case MATH_DECTIME: { - if (p_idx == 0) { - return PropertyInfo(Variant::FLOAT, "value"); - } else if (p_idx == 1) { - return PropertyInfo(Variant::FLOAT, "amount"); - } else { - return PropertyInfo(Variant::FLOAT, "step"); - } - } break; case MATH_RANDOMIZE: case MATH_RANDI: case MATH_RANDF: { @@ -536,10 +525,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_RANGE_LERP: case MATH_SMOOTHSTEP: case MATH_MOVE_TOWARD: - case MATH_DECTIME: { - t = Variant::FLOAT; - - } break; case MATH_RANDOMIZE: { } break; case MATH_RANDI: { @@ -837,12 +822,6 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in 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); - VALIDATE_ARG_NUM(1); - VALIDATE_ARG_NUM(2); - *r_return = Math::dectime((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); - } break; case VisualScriptBuiltinFunc::MATH_RANDOMIZE: { Math::randomize(); @@ -1239,7 +1218,6 @@ void VisualScriptBuiltinFunc::_bind_methods() { 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_RANDI); BIND_ENUM_CONSTANT(MATH_RANDF); @@ -1330,7 +1308,6 @@ void register_visual_script_builtin_func_node() { 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/randi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDI>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index 7196d4b46a..f59a7a0f0c 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -68,7 +68,6 @@ public: MATH_INVERSE_LERP, MATH_RANGE_LERP, MATH_MOVE_TOWARD, - MATH_DECTIME, MATH_RANDOMIZE, MATH_RANDI, MATH_RANDF, diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index fca7985b74..a802e8022d 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -984,7 +984,7 @@ void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, b return; } - undo_redo->create_action("Change Port Type"); + undo_redo->create_action(TTR("Change Port Type")); if (is_input) { undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_type", p_port, Variant::Type(p_select)); undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_type", p_port, vsn->get_input_value_port_info(p_port).type); @@ -1016,7 +1016,7 @@ void VisualScriptEditor::_port_name_focus_out(const Node *p_name_box, int p_id, return; } - undo_redo->create_action("Change Port Name"); + undo_redo->create_action(TTR("Change Port Name")); if (is_input) { undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_name", p_port, text); undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_name", p_port, vsn->get_input_value_port_info(p_port).name); diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml index f3e13ba31a..3b9acfd873 100644 --- a/modules/webm/doc_classes/VideoStreamWebm.xml +++ b/modules/webm/doc_classes/VideoStreamWebm.xml @@ -12,17 +12,14 @@ </tutorials> <methods> <method name="get_file"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the WebM video file handled by this [VideoStreamWebm]. </description> </method> <method name="set_file"> - <return type="void"> - </return> - <argument index="0" name="file" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="file" type="String" /> <description> Sets the WebM video file that this [VideoStreamWebm] resource handles. The [code]file[/code] name should have the [code].webm[/code] extension. </description> diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml index 3435dda982..cf5735bab5 100644 --- a/modules/webrtc/doc_classes/WebRTCDataChannel.xml +++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml @@ -8,88 +8,76 @@ </tutorials> <methods> <method name="close"> - <return type="void"> - </return> + <return type="void" /> <description> Closes this data channel, notifying the other peer. </description> </method> <method name="get_buffered_amount" qualifiers="const"> - <return type="int"> - </return> + <return type="int" /> <description> Returns the number of bytes currently queued to be sent over this channel. </description> </method> <method name="get_id" qualifiers="const"> - <return type="int"> - </return> + <return type="int" /> <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> + <return type="String" /> <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> + <return type="int" /> <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> + <return type="int" /> <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> + <return type="String" /> <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> + <return type="int" enum="WebRTCDataChannel.ChannelState" /> <description> Returns the current state of this channel, see [enum ChannelState]. </description> </method> <method name="is_negotiated" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <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> + <return type="bool" /> <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> + <return type="int" enum="Error" /> <description> Reserved, but not used for now. </description> </method> <method name="was_string_packet" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <description> Returns [code]true[/code] if the last received packet was transferred as text. See [member write_mode]. </description> diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml index 26c5bfa6ce..0c4a0d4ea0 100644 --- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml +++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml @@ -12,58 +12,45 @@ </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> + <return type="int" enum="Error" /> + <argument index="0" name="peer" type="WebRTCPeerConnection" /> + <argument index="1" name="peer_id" type="int" /> + <argument index="2" name="unreliable_lifetime" type="int" default="1" /> <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> + <return type="void" /> <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> + <return type="Dictionary" /> + <argument index="0" name="peer_id" type="int" /> <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> + <return type="Dictionary" /> <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> + <return type="bool" /> + <argument index="0" name="peer_id" type="int" /> <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> + <return type="int" enum="Error" /> + <argument index="0" name="peer_id" type="int" /> + <argument index="1" name="server_compatibility" type="bool" default="false" /> <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 MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted. @@ -71,10 +58,8 @@ </description> </method> <method name="remove_peer"> - <return type="void"> - </return> - <argument index="0" name="peer_id" type="int"> - </argument> + <return type="void" /> + <argument index="0" name="peer_id" type="int" /> <description> Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal MultiplayerPeer.peer_connected] was emitted for it, then [signal MultiplayerPeer.peer_disconnected] will be emitted. </description> diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index 62e524825d..f6f360503f 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -15,33 +15,25 @@ </tutorials> <methods> <method name="add_ice_candidate"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="media" type="String"> - </argument> - <argument index="1" name="index" type="int"> - </argument> - <argument index="2" name="name" type="String"> - </argument> + <return type="int" enum="Error" /> + <argument index="0" name="media" type="String" /> + <argument index="1" name="index" type="int" /> + <argument index="2" name="name" type="String" /> <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> + <return type="void" /> <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"> - <return type="WebRTCDataChannel"> - </return> - <argument index="0" name="label" type="String"> - </argument> + <return type="WebRTCDataChannel" /> + <argument index="0" name="label" type="String" /> <argument index="1" name="options" type="Dictionary" default="{ -}"> - </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]. @@ -63,26 +55,22 @@ </description> </method> <method name="create_offer"> - <return type="int" enum="Error"> - </return> + <return type="int" enum="Error" /> <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 [constant OK], [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> + <return type="int" enum="WebRTCPeerConnection.ConnectionState" /> <description> Returns the connection state. See [enum ConnectionState]. </description> </method> <method name="initialize"> - <return type="int" enum="Error"> - </return> + <return type="int" enum="Error" /> <argument index="0" name="configuration" type="Dictionary" default="{ -}"> - </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: @@ -103,31 +91,24 @@ </description> </method> <method name="poll"> - <return type="int" enum="Error"> - </return> + <return type="int" enum="Error" /> <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"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="type" type="String"> - </argument> - <argument index="1" name="sdp" type="String"> - </argument> + <return type="int" enum="Error" /> + <argument index="0" name="type" type="String" /> + <argument index="1" name="sdp" type="String" /> <description> Sets the SDP description of the local peer. This should be called in response to [signal session_description_created]. After calling this function the peer will start emitting [signal ice_candidate_created] (unless an [enum Error] different from [constant OK] is returned). </description> </method> <method name="set_remote_description"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="type" type="String"> - </argument> - <argument index="1" name="sdp" type="String"> - </argument> + <return type="int" enum="Error" /> + <argument index="0" name="type" type="String" /> + <argument index="1" name="sdp" type="String" /> <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. @@ -137,29 +118,23 @@ </methods> <signals> <signal name="data_channel_received"> - <argument index="0" name="channel" type="Object"> - </argument> + <argument index="0" name="channel" type="Object" /> <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"> - <argument index="0" name="media" type="String"> - </argument> - <argument index="1" name="index" type="int"> - </argument> - <argument index="2" name="name" type="String"> - </argument> + <argument index="0" name="media" type="String" /> + <argument index="1" name="index" type="int" /> + <argument index="2" name="name" type="String" /> <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"> - <argument index="0" name="type" type="String"> - </argument> - <argument index="1" name="sdp" type="String"> - </argument> + <argument index="0" name="type" type="String" /> + <argument index="1" name="sdp" type="String" /> <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> diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index 40c0ad17ad..1549a907b4 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -13,16 +13,11 @@ </tutorials> <methods> <method name="connect_to_url"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="url" type="String"> - </argument> - <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray()"> - </argument> - <argument index="2" name="gd_mp_api" type="bool" default="false"> - </argument> - <argument index="3" name="custom_headers" type="PackedStringArray" default="PackedStringArray()"> - </argument> + <return type="int" enum="Error" /> + <argument index="0" name="url" type="String" /> + <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray()" /> + <argument index="2" name="gd_mp_api" type="bool" default="false" /> + <argument index="3" name="custom_headers" type="PackedStringArray" default="PackedStringArray()" /> <description> Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested. If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted. @@ -33,26 +28,21 @@ </description> </method> <method name="disconnect_from_host"> - <return type="void"> - </return> - <argument index="0" name="code" type="int" default="1000"> - </argument> - <argument index="1" name="reason" type="String" default=""""> - </argument> + <return type="void" /> + <argument index="0" name="code" type="int" default="1000" /> + <argument index="1" name="reason" type="String" default="""" /> <description> Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information. </description> </method> <method name="get_connected_host" qualifiers="const"> - <return type="String"> - </return> + <return type="String" /> <description> Return the IP address of the currently connected host. </description> </method> <method name="get_connected_port" qualifiers="const"> - <return type="int"> - </return> + <return type="int" /> <description> Return the IP port of the currently connected host. </description> @@ -70,8 +60,7 @@ </members> <signals> <signal name="connection_closed"> - <argument index="0" name="was_clean_close" type="bool"> - </argument> + <argument index="0" name="was_clean_close" type="bool" /> <description> Emitted when the connection to the server is closed. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly. </description> @@ -82,8 +71,7 @@ </description> </signal> <signal name="connection_established"> - <argument index="0" name="protocol" type="String"> - </argument> + <argument index="0" name="protocol" type="String" /> <description> Emitted when a connection with the server is established, [code]protocol[/code] will contain the sub-protocol agreed with the server. </description> @@ -95,10 +83,8 @@ </description> </signal> <signal name="server_close_request"> - <argument index="0" name="code" type="int"> - </argument> - <argument index="1" name="reason" type="String"> - </argument> + <argument index="0" name="code" type="int" /> + <argument index="1" name="reason" type="String" /> <description> Emitted when the server requests a clean close. You should keep polling until you get a [signal connection_closed] signal to achieve the clean close. See [method WebSocketPeer.close] for more details. </description> diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index ee1b60f739..cd41e9a1fb 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -10,25 +10,18 @@ </tutorials> <methods> <method name="get_peer" qualifiers="const"> - <return type="WebSocketPeer"> - </return> - <argument index="0" name="peer_id" type="int"> - </argument> + <return type="WebSocketPeer" /> + <argument index="0" name="peer_id" type="int" /> <description> 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> + <return type="int" enum="Error" /> + <argument index="0" name="input_buffer_size_kb" type="int" /> + <argument index="1" name="input_max_packets" type="int" /> + <argument index="2" name="output_buffer_size_kb" type="int" /> + <argument index="3" name="output_max_packets" type="int" /> <description> Configures the buffer sizes for this WebSocket peer. Default values can be specified in the 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. @@ -43,8 +36,7 @@ </members> <signals> <signal name="peer_packet"> - <argument index="0" name="peer_source" type="int"> - </argument> + <argument index="0" name="peer_source" type="int" /> <description> Emitted when a packet is received from a peer. [b]Note:[/b] This signal is only emitted when the client or server is configured to use Godot multiplayer API. diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index 5125956416..ab7ef6c4d0 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -11,12 +11,9 @@ </tutorials> <methods> <method name="close"> - <return type="void"> - </return> - <argument index="0" name="code" type="int" default="1000"> - </argument> - <argument index="1" name="reason" type="String" default=""""> - </argument> + <return type="void" /> + <argument index="0" name="code" type="int" default="1000" /> + <argument index="1" name="reason" type="String" default="""" /> <description> Closes this WebSocket connection. [code]code[/code] is the status code for the closure (see RFC 6455 section 7.4 for a list of valid status codes). [code]reason[/code] is the human readable reason for closing the connection (can be any UTF-8 string that's smaller than 123 bytes). [b]Note:[/b] To achieve a clean close, you will need to keep polling until either [signal WebSocketClient.connection_closed] or [signal WebSocketServer.client_disconnected] is received. @@ -24,57 +21,54 @@ </description> </method> <method name="get_connected_host" qualifiers="const"> - <return type="String"> - </return> + <return type="String" /> <description> Returns the IP address of the connected peer. [b]Note:[/b] Not available in the HTML5 export. </description> </method> <method name="get_connected_port" qualifiers="const"> - <return type="int"> - </return> + <return type="int" /> <description> Returns the remote port of the connected peer. [b]Note:[/b] Not available in the HTML5 export. </description> </method> + <method name="get_current_outbound_buffered_amount" qualifiers="const"> + <return type="int" /> + <description> + Returns the current amount of data in the outbound websocket buffer. [b]Note:[/b] HTML5 exports use WebSocket.bufferedAmount, while other platforms use an internal buffer. + </description> + </method> <method name="get_write_mode" qualifiers="const"> - <return type="int" enum="WebSocketPeer.WriteMode"> - </return> + <return type="int" enum="WebSocketPeer.WriteMode" /> <description> Gets the current selected write mode. See [enum WriteMode]. </description> </method> <method name="is_connected_to_host" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <description> Returns [code]true[/code] if this peer is currently connected. </description> </method> <method name="set_no_delay"> - <return type="void"> - </return> - <argument index="0" name="enabled" type="bool"> - </argument> + <return type="void" /> + <argument index="0" name="enabled" type="bool" /> <description> Disable Nagle's algorithm on the underling TCP socket (default). See [method StreamPeerTCP.set_no_delay] for more information. [b]Note:[/b] Not available in the HTML5 export. </description> </method> <method name="set_write_mode"> - <return type="void"> - </return> - <argument index="0" name="mode" type="int" enum="WebSocketPeer.WriteMode"> - </argument> + <return type="void" /> + <argument index="0" name="mode" type="int" enum="WebSocketPeer.WriteMode" /> <description> Sets the socket to use the given [enum WriteMode]. </description> </method> <method name="was_string_packet" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <description> Returns [code]true[/code] if the last received packet was sent as a text payload. See [enum WriteMode]. </description> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 5491f7de15..90182de4c2 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -12,61 +12,46 @@ </tutorials> <methods> <method name="disconnect_peer"> - <return type="void"> - </return> - <argument index="0" name="id" type="int"> - </argument> - <argument index="1" name="code" type="int" default="1000"> - </argument> - <argument index="2" name="reason" type="String" default=""""> - </argument> + <return type="void" /> + <argument index="0" name="id" type="int" /> + <argument index="1" name="code" type="int" default="1000" /> + <argument index="2" name="reason" type="String" default="""" /> <description> Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more information. </description> </method> <method name="get_peer_address" qualifiers="const"> - <return type="String"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <return type="String" /> + <argument index="0" name="id" type="int" /> <description> Returns the IP address of the given peer. </description> </method> <method name="get_peer_port" qualifiers="const"> - <return type="int"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <return type="int" /> + <argument index="0" name="id" type="int" /> <description> Returns the remote port of the given peer. </description> </method> <method name="has_peer" qualifiers="const"> - <return type="bool"> - </return> - <argument index="0" name="id" type="int"> - </argument> + <return type="bool" /> + <argument index="0" name="id" type="int" /> <description> Returns [code]true[/code] if a peer with the given ID is connected. </description> </method> <method name="is_listening" qualifiers="const"> - <return type="bool"> - </return> + <return type="bool" /> <description> Returns [code]true[/code] if the server is actively listening on a port. </description> </method> <method name="listen"> - <return type="int" enum="Error"> - </return> - <argument index="0" name="port" type="int"> - </argument> - <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray()"> - </argument> - <argument index="2" name="gd_mp_api" type="bool" default="false"> - </argument> + <return type="int" enum="Error" /> + <argument index="0" name="port" type="int" /> + <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray()" /> + <argument index="2" name="gd_mp_api" type="bool" default="false" /> <description> Starts listening on the given port. You can specify the desired subprotocols via the "protocols" array. If the list empty (default), no sub-protocol will be requested. @@ -75,8 +60,7 @@ </description> </method> <method name="stop"> - <return type="void"> - </return> + <return type="void" /> <description> Stops the server and clear its state. </description> @@ -101,40 +85,31 @@ </members> <signals> <signal name="client_close_request"> - <argument index="0" name="id" type="int"> - </argument> - <argument index="1" name="code" type="int"> - </argument> - <argument index="2" name="reason" type="String"> - </argument> + <argument index="0" name="id" type="int" /> + <argument index="1" name="code" type="int" /> + <argument index="2" name="reason" type="String" /> <description> Emitted when a client requests a clean close. You should keep polling until you get a [signal client_disconnected] signal with the same [code]id[/code] to achieve the clean close. See [method WebSocketPeer.close] for more details. </description> </signal> <signal name="client_connected"> - <argument index="0" name="id" type="int"> - </argument> - <argument index="1" name="protocol" type="String"> - </argument> - <argument index="2" name="resource_name" type="String"> - </argument> + <argument index="0" name="id" type="int" /> + <argument index="1" name="protocol" type="String" /> + <argument index="2" name="resource_name" type="String" /> <description> Emitted when a new client connects. "protocol" will be the sub-protocol agreed with the client, and "resource_name" will be the resource name of the URI the peer used. "resource_name" is a path (at the very least a single forward slash) and potentially a query string. </description> </signal> <signal name="client_disconnected"> - <argument index="0" name="id" type="int"> - </argument> - <argument index="1" name="was_clean_close" type="bool"> - </argument> + <argument index="0" name="id" type="int" /> + <argument index="1" name="was_clean_close" type="bool" /> <description> Emitted when a client disconnects. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly. </description> </signal> <signal name="data_received"> - <argument index="0" name="id" type="int"> - </argument> + <argument index="0" name="id" type="int" /> <description> Emitted when a new message is received. [b]Note:[/b] This signal is [i]not[/i] emitted when used as high-level multiplayer peer. diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index d3d0066c12..5cd94e978f 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -95,7 +95,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, return FAILED; } - static_cast<Ref<EMWSPeer>>(_peer)->set_sock(_js_id, _in_buf_size, _in_pkt_size); + static_cast<Ref<EMWSPeer>>(_peer)->set_sock(_js_id, _in_buf_size, _in_pkt_size, _out_buf_size); return OK; } @@ -136,6 +136,7 @@ int EMWSClient::get_max_packet_size() const { 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); + _out_buf_size = nearest_shift(p_out_buffer - 1) + 10; return OK; } diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index ca2d7ed986..3b0b8395b9 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -45,6 +45,7 @@ private: bool _is_connecting = false; int _in_buf_size = DEF_BUF_SHIFT; int _in_pkt_size = DEF_PKT_SHIFT; + int _out_buf_size = DEF_BUF_SHIFT; static void _esws_on_connect(void *obj, char *proto); static void _esws_on_message(void *obj, const uint8_t *p_data, int p_data_size, int p_is_string); diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp index 05f9e12ae1..69822f6ff3 100644 --- a/modules/websocket/emws_peer.cpp +++ b/modules/websocket/emws_peer.cpp @@ -33,10 +33,11 @@ #include "emws_peer.h" #include "core/io/ip.h" -void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size) { +void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size) { peer_sock = p_sock; _in_buffer.resize(p_in_pkt_size, p_in_buf_size); _packet_buffer.resize((1 << p_in_buf_size)); + _out_buf_size = p_out_buf_size; } void EMWSPeer::set_write_mode(WriteMode p_mode) { @@ -53,7 +54,10 @@ Error EMWSPeer::read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_strin } Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(_out_buf_size && (godot_js_websocket_buffered_amount(peer_sock) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); + int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0; + godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin); return OK; } @@ -76,6 +80,13 @@ int EMWSPeer::get_available_packet_count() const { return _in_buffer.packets_left(); } +int EMWSPeer::get_current_outbound_buffered_amount() const { + if (peer_sock != -1) { + return godot_js_websocket_buffered_amount(peer_sock); + } + return 0; +} + bool EMWSPeer::was_string_packet() const { return _is_string; } diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h index 73e701720b..6e93ea31a2 100644 --- a/modules/websocket/emws_peer.h +++ b/modules/websocket/emws_peer.h @@ -48,6 +48,7 @@ typedef void (*WSOnError)(void *p_ref); extern int godot_js_websocket_create(void *p_ref, const char *p_url, const char *p_proto, WSOnOpen p_on_open, WSOnMessage p_on_message, WSOnError p_on_error, WSOnClose p_on_close); extern int godot_js_websocket_send(int p_id, const uint8_t *p_buf, int p_buf_len, int p_raw); +extern int godot_js_websocket_buffered_amount(int p_id); extern void godot_js_websocket_close(int p_id, int p_code, const char *p_reason); extern void godot_js_websocket_destroy(int p_id); } @@ -62,14 +63,16 @@ private: Vector<uint8_t> _packet_buffer; PacketBuffer<uint8_t> _in_buffer; uint8_t _is_string = 0; + int _out_buf_size = 0; public: Error read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_string); - void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size); + void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size); virtual int get_available_packet_count() const; virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); virtual int get_max_packet_size() const { return _packet_buffer.size(); }; + virtual int get_current_outbound_buffered_amount() const; virtual void close(int p_code = 1000, String p_reason = ""); virtual bool is_connected_to_host() const; diff --git a/modules/websocket/library_godot_websocket.js b/modules/websocket/library_godot_websocket.js index b182d1ecde..dd2fd1e94f 100644 --- a/modules/websocket/library_godot_websocket.js +++ b/modules/websocket/library_godot_websocket.js @@ -101,6 +101,15 @@ const GodotWebSocket = { return 0; }, + // Get current bufferedAmount + bufferedAmount: function (p_id) { + const ref = IDHandler.get(p_id); + if (!ref) { + return 0; // Godot object is gone. + } + return ref.bufferedAmount; + }, + create: function (socket, p_on_open, p_on_message, p_on_error, p_on_close) { const id = IDHandler.add(socket); socket.onopen = GodotWebSocket._onopen.bind(null, id, p_on_open); @@ -171,6 +180,11 @@ const GodotWebSocket = { return GodotWebSocket.send(p_id, out); }, + godot_js_websocket_buffered_amount__sig: 'ii', + godot_js_websocket_buffered_amount: function (p_id) { + return GodotWebSocket.bufferedAmount(p_id); + }, + godot_js_websocket_close__sig: 'viii', godot_js_websocket_close: function (p_id, p_code, p_reason) { const code = p_code; diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 589bb8931a..52d9a602a1 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -39,26 +39,6 @@ WebSocketMultiplayerPeer::~WebSocketMultiplayerPeer() { _clear(); } -int WebSocketMultiplayerPeer::_gen_unique_id() const { - uint32_t hash = 0; - - while (hash == 0 || hash == 1) { - hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_ticks_usec()); - hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_unix_time(), hash); - hash = hash_djb2_one_32( - (uint32_t)OS::get_singleton()->get_data_path().hash64(), hash); - hash = hash_djb2_one_32( - (uint32_t)((uint64_t)this), hash); //rely on aslr heap - hash = hash_djb2_one_32( - (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack - hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negative id is used for exclusion - } - - return hash; -} - void WebSocketMultiplayerPeer::_clear() { _peer_map.clear(); if (_current_packet.data != nullptr) { diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h index e3ccd1a795..4e80f876d6 100644 --- a/modules/websocket/websocket_multiplayer_peer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -75,7 +75,6 @@ protected: void _send_add(int32_t p_peer_id); void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id); void _send_del(int32_t p_peer_id); - int _gen_unique_id() const; public: /* MultiplayerPeer */ diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp index e77fdcfed2..ee13040821 100644 --- a/modules/websocket/websocket_peer.cpp +++ b/modules/websocket/websocket_peer.cpp @@ -47,6 +47,7 @@ void WebSocketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host); ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port); ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &WebSocketPeer::set_no_delay); + ClassDB::bind_method(D_METHOD("get_current_outbound_buffered_amount"), &WebSocketPeer::get_current_outbound_buffered_amount); BIND_ENUM_CONSTANT(WRITE_MODE_TEXT); BIND_ENUM_CONSTANT(WRITE_MODE_BINARY); diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h index e9bb20f21f..517b8600d6 100644 --- a/modules/websocket/websocket_peer.h +++ b/modules/websocket/websocket_peer.h @@ -59,6 +59,7 @@ public: virtual uint16_t get_connected_port() const = 0; virtual bool was_string_packet() const = 0; virtual void set_no_delay(bool p_enabled) = 0; + virtual int get_current_outbound_buffered_amount() const = 0; WebSocketPeer(); ~WebSocketPeer(); diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 1dbadfed74..7f027e1c0d 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -205,7 +205,9 @@ void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigne ERR_FAIL_COND(p_data == nullptr); _in_buffer.resize(p_in_pkt_size, p_in_buf_size); - _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size))); + _packet_buffer.resize(1 << p_in_buf_size); + _out_buf_size = p_out_buf_size; + _out_pkt_size = p_out_pkt_size; _data = p_data; _data->peer = this; @@ -239,6 +241,8 @@ void WSLPeer::poll() { Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(!is_connected_to_host(), FAILED); + ERR_FAIL_COND_V(_out_pkt_size && (wslay_event_get_queued_msg_count(_data->ctx) >= (1ULL << _out_pkt_size)), ERR_OUT_OF_MEMORY); + ERR_FAIL_COND_V(_out_buf_size && (wslay_event_get_queued_msg_length(_data->ctx) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY); struct wslay_event_msg msg; // Should I use fragmented? msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME; @@ -280,6 +284,12 @@ int WSLPeer::get_available_packet_count() const { return _in_buffer.packets_left(); } +int WSLPeer::get_current_outbound_buffered_amount() const { + ERR_FAIL_COND_V(!_data, 0); + + return wslay_event_get_queued_msg_length(_data->ctx); +} + bool WSLPeer::was_string_packet() const { return _is_string; } diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h index f1ea98d384..260d4b183d 100644 --- a/modules/websocket/wsl_peer.h +++ b/modules/websocket/wsl_peer.h @@ -77,6 +77,9 @@ private: WriteMode write_mode = WRITE_MODE_BINARY; + int _out_buf_size = 0; + int _out_pkt_size = 0; + public: int close_code = -1; String close_reason; @@ -86,6 +89,7 @@ public: virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); virtual int get_max_packet_size() const { return _packet_buffer.size(); }; + virtual int get_current_outbound_buffered_amount() const; virtual void close_now(); virtual void close(int p_code = 1000, String p_reason = ""); diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index c6eb3d44b4..7402bbb46e 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -207,7 +207,7 @@ void WSLServer::poll() { continue; } // Creating new peer - int32_t id = _gen_unique_id(); + int32_t id = generate_unique_id(); WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); data->obj = this; diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml index 67b2b866f3..ff7c46bbae 100644 --- a/modules/webxr/doc_classes/WebXRInterface.xml +++ b/modules/webxr/doc_classes/WebXRInterface.xml @@ -95,10 +95,8 @@ </tutorials> <methods> <method name="get_controller" qualifiers="const"> - <return type="XRPositionalTracker"> - </return> - <argument index="0" name="controller_id" type="int"> - </argument> + <return type="XRPositionalTracker" /> + <argument index="0" name="controller_id" type="int" /> <description> Gets an [XRPositionalTracker] for the given [code]controller_id[/code]. In the context of WebXR, a "controller" can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional controller is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with. @@ -112,10 +110,8 @@ </description> </method> <method name="is_session_supported"> - <return type="void"> - </return> - <argument index="0" name="session_mode" type="String"> - </argument> + <return type="void" /> + <argument index="0" name="session_mode" type="String" /> <description> Checks if the given [code]session_mode[/code] is supported by the user's browser. Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode]WebXR's XRSessionMode[/url], including: [code]"immersive-vr"[/code], [code]"immersive-ar"[/code], and [code]"inline"[/code]. @@ -170,24 +166,21 @@ </description> </signal> <signal name="select"> - <argument index="0" name="controller_id" type="int"> - </argument> + <argument index="0" name="controller_id" type="int" /> <description> Emitted after one of the "controllers" has finished its "primary action". Use [method get_controller] to get more information about the controller. </description> </signal> <signal name="selectend"> - <argument index="0" name="controller_id" type="int"> - </argument> + <argument index="0" name="controller_id" type="int" /> <description> Emitted when one of the "controllers" has finished its "primary action". Use [method get_controller] to get more information about the controller. </description> </signal> <signal name="selectstart"> - <argument index="0" name="controller_id" type="int"> - </argument> + <argument index="0" name="controller_id" type="int" /> <description> Emitted when one of the "controllers" has started its "primary action". Use [method get_controller] to get more information about the controller. @@ -200,8 +193,7 @@ </description> </signal> <signal name="session_failed"> - <argument index="0" name="message" type="String"> - </argument> + <argument index="0" name="message" type="String" /> <description> Emitted by [method XRInterface.initialize] if the session fails to start. [code]message[/code] may optionally contain an error message from WebXR, or an empty string if no message is available. @@ -214,33 +206,28 @@ </description> </signal> <signal name="session_supported"> - <argument index="0" name="session_mode" type="String"> - </argument> - <argument index="1" name="supported" type="bool"> - </argument> + <argument index="0" name="session_mode" type="String" /> + <argument index="1" name="supported" type="bool" /> <description> Emitted by [method is_session_supported] to indicate if the given [code]session_mode[/code] is supported or not. </description> </signal> <signal name="squeeze"> - <argument index="0" name="controller_id" type="int"> - </argument> + <argument index="0" name="controller_id" type="int" /> <description> Emitted after one of the "controllers" has finished its "primary squeeze action". Use [method get_controller] to get more information about the controller. </description> </signal> <signal name="squeezeend"> - <argument index="0" name="controller_id" type="int"> - </argument> + <argument index="0" name="controller_id" type="int" /> <description> Emitted when one of the "controllers" has finished its "primary squeeze action". Use [method get_controller] to get more information about the controller. </description> </signal> <signal name="squeezestart"> - <argument index="0" name="controller_id" type="int"> - </argument> + <argument index="0" name="controller_id" type="int" /> <description> Emitted when one of the "controllers" has started its "primary squeeze action". Use [method get_controller] to get more information about the controller. |