initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
44
modules/enet/SCsub
Normal file
44
modules/enet/SCsub
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
||||
env_enet = env_modules.Clone()
|
||||
|
||||
# Thirdparty source files
|
||||
|
||||
thirdparty_obj = []
|
||||
|
||||
if env["builtin_enet"]:
|
||||
thirdparty_dir = "#thirdparty/enet/"
|
||||
thirdparty_sources = [
|
||||
"enet_godot.cpp",
|
||||
"callbacks.c",
|
||||
"compress.c",
|
||||
"host.c",
|
||||
"list.c",
|
||||
"packet.c",
|
||||
"peer.c",
|
||||
"protocol.c",
|
||||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
|
||||
env_enet.Prepend(CPPEXTPATH=[thirdparty_dir])
|
||||
env_enet.Append(CPPDEFINES=["GODOT_ENET"])
|
||||
|
||||
env_thirdparty = env_enet.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
|
||||
env.modules_sources += thirdparty_obj
|
||||
|
||||
|
||||
# Godot source files
|
||||
|
||||
module_obj = []
|
||||
|
||||
env_enet.add_source_files(module_obj, "*.cpp")
|
||||
env.modules_sources += module_obj
|
||||
|
||||
# Needed to force rebuilding the module files when the thirdparty library is updated.
|
||||
env.Depends(module_obj, thirdparty_obj)
|
18
modules/enet/config.py
Normal file
18
modules/enet/config.py
Normal file
@@ -0,0 +1,18 @@
|
||||
def can_build(env, platform):
|
||||
return True
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
||||
|
||||
|
||||
def get_doc_classes():
|
||||
return [
|
||||
"ENetMultiplayerPeer",
|
||||
"ENetConnection",
|
||||
"ENetPacketPeer",
|
||||
]
|
||||
|
||||
|
||||
def get_doc_path():
|
||||
return "doc_classes"
|
208
modules/enet/doc_classes/ENetConnection.xml
Normal file
208
modules/enet/doc_classes/ENetConnection.xml
Normal file
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="ENetConnection" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||
<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" />
|
||||
<param index="0" name="in_bandwidth" type="int" default="0" />
|
||||
<param 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" />
|
||||
<param index="0" name="channel" type="int" />
|
||||
<param index="1" name="packet" type="PackedByteArray" />
|
||||
<param index="2" name="flags" type="int" />
|
||||
<description>
|
||||
Queues a [param packet] to be sent to all peers associated with the host over the specified [param channel]. See [ENetPacketPeer] [code]FLAG_*[/code] constants for available packet flags.
|
||||
</description>
|
||||
</method>
|
||||
<method name="channel_limit">
|
||||
<return type="void" />
|
||||
<param index="0" name="limit" type="int" />
|
||||
<description>
|
||||
Limits the maximum allowed channels of future incoming connections.
|
||||
</description>
|
||||
</method>
|
||||
<method name="compress">
|
||||
<return type="void" />
|
||||
<param 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.
|
||||
[b]Note:[/b] The compression mode must be set to the same value on both the server and all its clients. Clients will fail to connect if the compression mode set on the client differs from the one set on the server.
|
||||
</description>
|
||||
</method>
|
||||
<method name="connect_to_host">
|
||||
<return type="ENetPacketPeer" />
|
||||
<param index="0" name="address" type="String" />
|
||||
<param index="1" name="port" type="int" />
|
||||
<param index="2" name="channels" type="int" default="0" />
|
||||
<param index="3" name="data" type="int" default="0" />
|
||||
<description>
|
||||
Initiates a connection to a foreign [param address] using the specified [param port] and allocating the requested [param channels]. Optional [param data] can be passed during connection in the form of a 32 bit integer.
|
||||
[b]Note:[/b] You must call either [method create_host] or [method create_host_bound] on both ends before calling this method.
|
||||
</description>
|
||||
</method>
|
||||
<method name="create_host">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="max_peers" type="int" default="32" />
|
||||
<param index="1" name="max_channels" type="int" default="0" />
|
||||
<param index="2" name="in_bandwidth" type="int" default="0" />
|
||||
<param index="3" name="out_bandwidth" type="int" default="0" />
|
||||
<description>
|
||||
Creates an ENetHost that allows up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth] (if greater than zero).
|
||||
This method binds a random available dynamic UDP port on the host machine at the [i]unspecified[/i] address. Use [method create_host_bound] to specify the address and port.
|
||||
[b]Note:[/b] It is necessary to create a host in both client and server in order to establish a connection.
|
||||
</description>
|
||||
</method>
|
||||
<method name="create_host_bound">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="bind_address" type="String" />
|
||||
<param index="1" name="bind_port" type="int" />
|
||||
<param index="2" name="max_peers" type="int" default="32" />
|
||||
<param index="3" name="max_channels" type="int" default="0" />
|
||||
<param index="4" name="in_bandwidth" type="int" default="0" />
|
||||
<param index="5" name="out_bandwidth" type="int" default="0" />
|
||||
<description>
|
||||
Creates an ENetHost bound to the given [param bind_address] and [param bind_port] that allows up to [param max_peers] connected peers, each allocating up to [param max_channels] channels, optionally limiting bandwidth to [param in_bandwidth] and [param out_bandwidth] (if greater than zero).
|
||||
[b]Note:[/b] It is necessary to create a host in both client and server in order to establish a connection.
|
||||
</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" />
|
||||
<param index="0" name="hostname" type="String" />
|
||||
<param index="1" name="client_options" type="TLSOptions" default="null" />
|
||||
<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 validating the server certificate against [param hostname]. You can pass the optional [param client_options] parameter to customize the trusted certification authorities, or disable the common name verification. See [method TLSOptions.client] and [method TLSOptions.client_unsafe].
|
||||
</description>
|
||||
</method>
|
||||
<method name="dtls_server_setup">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="server_options" type="TLSOptions" />
|
||||
<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. See [method TLSOptions.server].
|
||||
</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="ENetPacketPeer[]" />
|
||||
<description>
|
||||
Returns the list of peers associated with this host.
|
||||
[b]Note:[/b] 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" />
|
||||
<param index="0" name="statistic" type="int" enum="ENetConnection.HostStatistic" />
|
||||
<description>
|
||||
Returns and resets host statistics.
|
||||
</description>
|
||||
</method>
|
||||
<method name="refuse_new_connections">
|
||||
<return type="void" />
|
||||
<param index="0" name="refuse" type="bool" />
|
||||
<description>
|
||||
Configures the DTLS server to automatically drop new connections.
|
||||
[b]Note:[/b] This method is only relevant after calling [method dtls_server_setup].
|
||||
</description>
|
||||
</method>
|
||||
<method name="service">
|
||||
<return type="Array" />
|
||||
<param index="0" name="timeout" type="int" default="0" />
|
||||
<description>
|
||||
Waits for events on this connection and shuttles packets between the host and its peers, with the given [param timeout] (in milliseconds). 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.
|
||||
[b]Note:[/b] This method must be called on both ends involved in the event (sending and receiving hosts).
|
||||
</description>
|
||||
</method>
|
||||
<method name="socket_send">
|
||||
<return type="void" />
|
||||
<param index="0" name="destination_address" type="String" />
|
||||
<param index="1" name="destination_port" type="int" />
|
||||
<param index="2" name="packet" type="PackedByteArray" />
|
||||
<description>
|
||||
Sends a [param packet] toward a destination from the address and port currently bound by this ENetConnection instance.
|
||||
This is useful as it serves to establish entries in NAT routing tables on all devices between this bound instance and the public facing internet, allowing a prospective client's connection packets to be routed backward through the NAT device(s) between the public internet and this host.
|
||||
This requires forward knowledge of a prospective client's address and communication port as seen by the public internet - after any NAT devices have handled their connection request. This information can be obtained by a [url=https://en.wikipedia.org/wiki/STUN]STUN[/url] service, and must be handed off to your host by an entity that is not the prospective client. This will never work for a client behind a Symmetric NAT due to the nature of the Symmetric NAT routing algorithm, as their IP and Port cannot be known beforehand.
|
||||
</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=https://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 initialized 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 and the channel number upon which the packet was received. The received packet will be queued to the associated [ENetPacketPeer].
|
||||
</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>
|
74
modules/enet/doc_classes/ENetMultiplayerPeer.xml
Normal file
74
modules/enet/doc_classes/ENetMultiplayerPeer.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="ENetMultiplayerPeer" inherits="MultiplayerPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||
<brief_description>
|
||||
A MultiplayerPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library.
|
||||
</brief_description>
|
||||
<description>
|
||||
A MultiplayerPeer implementation that should be passed to [member MultiplayerAPI.multiplayer_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>
|
||||
<link title="High-level multiplayer">$DOCS_URL/tutorials/networking/high_level_multiplayer.html</link>
|
||||
<link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="add_mesh_peer">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="peer_id" type="int" />
|
||||
<param index="1" name="host" type="ENetConnection" />
|
||||
<description>
|
||||
Add a new remote peer with the given [param peer_id] connected to the given [param host].
|
||||
[b]Note:[/b] The [param host] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state.
|
||||
</description>
|
||||
</method>
|
||||
<method name="create_client">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="address" type="String" />
|
||||
<param index="1" name="port" type="int" />
|
||||
<param index="2" name="channel_count" type="int" default="0" />
|
||||
<param index="3" name="in_bandwidth" type="int" default="0" />
|
||||
<param index="4" name="out_bandwidth" type="int" default="0" />
|
||||
<param index="5" name="local_port" type="int" default="0" />
|
||||
<description>
|
||||
Create client that connects to a server at [param address] using specified [param port]. 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 [param port] is the port the server is listening on. The [param channel_count] parameter can be used to specify the number of ENet channels allocated for the connection. The [param in_bandwidth] and [param out_bandwidth] 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 MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [param local_port] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques.
|
||||
</description>
|
||||
</method>
|
||||
<method name="create_mesh">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="unique_id" type="int" />
|
||||
<description>
|
||||
Initialize this [MultiplayerPeer] in mesh mode. The provided [param unique_id] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.multiplayer_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="create_server">
|
||||
<return type="int" enum="Error" />
|
||||
<param index="0" name="port" type="int" />
|
||||
<param index="1" name="max_clients" type="int" default="32" />
|
||||
<param index="2" name="max_channels" type="int" default="0" />
|
||||
<param index="3" name="in_bandwidth" type="int" default="0" />
|
||||
<param index="4" name="out_bandwidth" type="int" default="0" />
|
||||
<description>
|
||||
Create server that listens to connections via [param port]. 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. [param max_clients] 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 MultiplayerPeer.close] first) or [constant ERR_CANT_CREATE] if the server could not be created.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_peer" qualifiers="const">
|
||||
<return type="ENetPacketPeer" />
|
||||
<param index="0" name="id" type="int" />
|
||||
<description>
|
||||
Returns the [ENetPacketPeer] associated to the given [param id].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_bind_ip">
|
||||
<return type="void" />
|
||||
<param 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>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="host" type="ENetConnection" setter="" getter="get_host">
|
||||
The underlying [ENetConnection] created after [method create_client] and [method create_server].
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
220
modules/enet/doc_classes/ENetPacketPeer.xml
Normal file
220
modules/enet/doc_classes/ENetPacketPeer.xml
Normal file
@@ -0,0 +1,220 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="ENetPacketPeer" inherits="PacketPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
|
||||
<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].
|
||||
[b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
|
||||
</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_packet_flags" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the ENet flags of the next packet in the received queue. See [code]FLAG_*[/code] constants for available packet flags. Note that not all flags are replicated from the sending peer to the receiving peer.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_remote_address" qualifiers="const">
|
||||
<return type="String" />
|
||||
<description>
|
||||
Returns the IP address of this peer.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_remote_port" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the remote port of this peer.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_state" qualifiers="const">
|
||||
<return type="int" enum="ENetPacketPeer.PeerState" />
|
||||
<description>
|
||||
Returns the current peer state.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_statistic">
|
||||
<return type="float" />
|
||||
<param index="0" name="statistic" type="int" enum="ENetPacketPeer.PeerStatistic" />
|
||||
<description>
|
||||
Returns the requested [param statistic] for this peer.
|
||||
</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" />
|
||||
<param 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" />
|
||||
<param 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" />
|
||||
<param 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" />
|
||||
<param index="0" name="ping_interval" type="int" />
|
||||
<description>
|
||||
Sets the [param ping_interval] 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. The default ping interval is [code]500[/code] milliseconds.
|
||||
</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" />
|
||||
<param index="0" name="channel" type="int" />
|
||||
<param index="1" name="packet" type="PackedByteArray" />
|
||||
<param index="2" name="flags" type="int" />
|
||||
<description>
|
||||
Queues a [param packet] to be sent over the specified [param channel]. See [code]FLAG_*[/code] constants for available packet flags.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_timeout">
|
||||
<return type="void" />
|
||||
<param index="0" name="timeout" type="int" />
|
||||
<param index="1" name="timeout_min" type="int" />
|
||||
<param 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 [param timeout] 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 [param timeout_min]. The [param timeout_max] 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" />
|
||||
<param index="0" name="interval" type="int" />
|
||||
<param index="1" name="acceleration" type="int" />
|
||||
<param 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 [param interval], ENet will either increase the probability by the amount specified in the [param acceleration] parameter, or decrease it by the amount specified in the [param deceleration] 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 [code]0[/code], 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">
|
||||
The peer is disconnected.
|
||||
</constant>
|
||||
<constant name="STATE_CONNECTING" value="1" enum="PeerState">
|
||||
The peer is currently attempting to connect.
|
||||
</constant>
|
||||
<constant name="STATE_ACKNOWLEDGING_CONNECT" value="2" enum="PeerState">
|
||||
The peer has acknowledged the connection request.
|
||||
</constant>
|
||||
<constant name="STATE_CONNECTION_PENDING" value="3" enum="PeerState">
|
||||
The peer is currently connecting.
|
||||
</constant>
|
||||
<constant name="STATE_CONNECTION_SUCCEEDED" value="4" enum="PeerState">
|
||||
The peer has successfully connected, but is not ready to communicate with yet ([constant STATE_CONNECTED]).
|
||||
</constant>
|
||||
<constant name="STATE_CONNECTED" value="5" enum="PeerState">
|
||||
The peer is currently connected and ready to communicate with.
|
||||
</constant>
|
||||
<constant name="STATE_DISCONNECT_LATER" value="6" enum="PeerState">
|
||||
The peer is expected to disconnect after it has no more outgoing packets to send.
|
||||
</constant>
|
||||
<constant name="STATE_DISCONNECTING" value="7" enum="PeerState">
|
||||
The peer is currently disconnecting.
|
||||
</constant>
|
||||
<constant name="STATE_ACKNOWLEDGING_DISCONNECT" value="8" enum="PeerState">
|
||||
The peer has acknowledged the disconnection request.
|
||||
</constant>
|
||||
<constant name="STATE_ZOMBIE" value="9" enum="PeerState">
|
||||
The peer has lost connection, but is not considered truly disconnected (as the peer didn't acknowledge the disconnection request).
|
||||
</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">
|
||||
The time at which packet loss statistics were last updated (in milliseconds since the connection started). The interval for packet loss statistics updates is 10 seconds, and at least one packet must have been sent since the last statistics update.
|
||||
</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">
|
||||
The peer's current throttle status.
|
||||
</constant>
|
||||
<constant name="PEER_PACKET_THROTTLE_LIMIT" value="8" enum="PeerStatistic">
|
||||
The maximum number of unreliable packets that should not be dropped. This value is always greater than or equal to [code]1[/code]. The initial value is equal to [constant PACKET_THROTTLE_SCALE].
|
||||
</constant>
|
||||
<constant name="PEER_PACKET_THROTTLE_COUNTER" value="9" enum="PeerStatistic">
|
||||
Internal value used to increment the packet throttle counter. The value is hardcoded to [code]7[/code] and cannot be changed. You probably want to look at [constant PEER_PACKET_THROTTLE_ACCELERATION] instead.
|
||||
</constant>
|
||||
<constant name="PEER_PACKET_THROTTLE_EPOCH" value="10" enum="PeerStatistic">
|
||||
The time at which throttle statistics were last updated (in milliseconds since the connection started). The interval for throttle statistics updates is [constant PEER_PACKET_THROTTLE_INTERVAL].
|
||||
</constant>
|
||||
<constant name="PEER_PACKET_THROTTLE_ACCELERATION" value="11" enum="PeerStatistic">
|
||||
The throttle's acceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]more[/i] often. The default value is [code]2[/code].
|
||||
</constant>
|
||||
<constant name="PEER_PACKET_THROTTLE_DECELERATION" value="12" enum="PeerStatistic">
|
||||
The throttle's deceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]less[/i] often. The default value is [code]2[/code].
|
||||
</constant>
|
||||
<constant name="PEER_PACKET_THROTTLE_INTERVAL" value="13" enum="PeerStatistic">
|
||||
The interval over which the lowest mean round trip time should be measured for use by the throttle mechanism (in milliseconds). The default value is [code]5000[/code].
|
||||
</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. The default value is [code]32[/code]. 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>
|
520
modules/enet/enet_connection.cpp
Normal file
520
modules/enet/enet_connection.cpp
Normal file
@@ -0,0 +1,520 @@
|
||||
/**************************************************************************/
|
||||
/* enet_connection.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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"
|
||||
#include "core/variant/typed_array.h"
|
||||
|
||||
void ENetConnection::broadcast(enet_uint8 p_channel, ENetPacket *p_packet) {
|
||||
ERR_FAIL_NULL_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_NULL_MSG(host, "Host already destroyed.");
|
||||
for (const Ref<ENetPacketPeer> &peer : peers) {
|
||||
peer->_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_NULL_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.instantiate(peer);
|
||||
peers.push_back(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
ENetConnection::EventType ENetConnection::_parse_event(const ENetEvent &p_event, Event &r_event) {
|
||||
switch (p_event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT: {
|
||||
if (p_event.peer->data == nullptr) {
|
||||
Ref<ENetPacketPeer> pp = memnew(ENetPacketPeer(p_event.peer));
|
||||
peers.push_back(pp);
|
||||
}
|
||||
r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
|
||||
r_event.data = p_event.data;
|
||||
return EVENT_CONNECT;
|
||||
} break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT: {
|
||||
// A peer disconnected.
|
||||
if (p_event.peer->data != nullptr) {
|
||||
Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
|
||||
pp->_on_disconnect();
|
||||
peers.erase(pp);
|
||||
r_event.peer = pp;
|
||||
r_event.data = p_event.data;
|
||||
return EVENT_DISCONNECT;
|
||||
}
|
||||
return EVENT_ERROR;
|
||||
} break;
|
||||
case ENET_EVENT_TYPE_RECEIVE: {
|
||||
// Packet received.
|
||||
if (p_event.peer->data != nullptr) {
|
||||
Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
|
||||
r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
|
||||
r_event.channel_id = p_event.channelID;
|
||||
r_event.packet = p_event.packet;
|
||||
return EVENT_RECEIVE;
|
||||
}
|
||||
return EVENT_ERROR;
|
||||
} break;
|
||||
case ENET_EVENT_TYPE_NONE:
|
||||
return EVENT_NONE;
|
||||
default:
|
||||
return EVENT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
ENetConnection::EventType ENetConnection::service(int p_timeout, Event &r_event) {
|
||||
ERR_FAIL_NULL_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;
|
||||
}
|
||||
return _parse_event(event, r_event);
|
||||
}
|
||||
|
||||
int ENetConnection::check_events(EventType &r_type, Event &r_event) {
|
||||
ERR_FAIL_NULL_V_MSG(host, -1, "The ENetConnection instance isn't currently active.");
|
||||
ENetEvent event;
|
||||
int ret = enet_host_check_events(host, &event);
|
||||
if (ret < 0) {
|
||||
r_type = EVENT_ERROR;
|
||||
return ret;
|
||||
}
|
||||
r_type = _parse_event(event, r_event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ENetConnection::flush() {
|
||||
ERR_FAIL_NULL_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_NULL_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_NULL_MSG(host, "The ENetConnection instance isn't currently active.");
|
||||
enet_host_channel_limit(host, p_max_channels);
|
||||
}
|
||||
|
||||
void ENetConnection::bandwidth_throttle() {
|
||||
ERR_FAIL_NULL_MSG(host, "The ENetConnection instance isn't currently active.");
|
||||
enet_host_bandwidth_throttle(host);
|
||||
}
|
||||
|
||||
void ENetConnection::compress(CompressionMode p_mode) {
|
||||
ERR_FAIL_NULL_MSG(host, "The ENetConnection instance isn't currently active.");
|
||||
Compressor::setup(host, p_mode);
|
||||
}
|
||||
|
||||
double ENetConnection::pop_statistic(HostStatistic p_stat) {
|
||||
ERR_FAIL_NULL_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_NULL_V_MSG(ptr, 0, "Invalid statistic: " + itos(p_stat) + ".");
|
||||
uint32_t ret = *ptr;
|
||||
*ptr = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ENetConnection::get_max_channels() const {
|
||||
ERR_FAIL_NULL_V_MSG(host, 0, "The ENetConnection instance isn't currently active.");
|
||||
return host->channelLimit;
|
||||
}
|
||||
|
||||
int ENetConnection::get_local_port() const {
|
||||
ERR_FAIL_NULL_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);
|
||||
}
|
||||
}
|
||||
|
||||
TypedArray<ENetPacketPeer> ENetConnection::_get_peers() {
|
||||
ERR_FAIL_NULL_V_MSG(host, Array(), "The ENetConnection instance isn't currently active.");
|
||||
TypedArray<ENetPacketPeer> out;
|
||||
for (const Ref<ENetPacketPeer> &I : peers) {
|
||||
out.push_back(I);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Error ENetConnection::dtls_server_setup(const Ref<TLSOptions> &p_options) {
|
||||
#ifdef GODOT_ENET
|
||||
ERR_FAIL_NULL_V_MSG(host, ERR_UNCONFIGURED, "The ENetConnection instance isn't currently active.");
|
||||
ERR_FAIL_COND_V(p_options.is_null() || !p_options->is_server(), ERR_INVALID_PARAMETER);
|
||||
return enet_host_dtls_server_setup(host, p_options.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_NULL_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(const String &p_hostname, const Ref<TLSOptions> &p_options) {
|
||||
#ifdef GODOT_ENET
|
||||
ERR_FAIL_NULL_V_MSG(host, ERR_UNCONFIGURED, "The ENetConnection instance isn't currently active.");
|
||||
ERR_FAIL_COND_V(p_options.is_null() || p_options->is_server(), ERR_INVALID_PARAMETER);
|
||||
return enet_host_dtls_client_setup(host, p_hostname.utf8().get_data(), p_options.ptr()) ? 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_NULL_V_MSG(host, ERR_CANT_CREATE, "Couldn't create an ENet host.");
|
||||
return OK;
|
||||
}
|
||||
|
||||
Array ENetConnection::_service(int p_timeout) {
|
||||
Event event;
|
||||
Ref<ENetPacketPeer> peer;
|
||||
EventType ret = service(p_timeout, event);
|
||||
Array out = { ret, event.peer, event.data, 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_NULL_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::socket_send(const String &p_address, int p_port, const PackedByteArray &p_packet) {
|
||||
ERR_FAIL_NULL_MSG(host, "The ENetConnection instance isn't currently active.");
|
||||
ERR_FAIL_COND_MSG(!(host->socket), "The ENetConnection instance isn't currently bound.");
|
||||
ERR_FAIL_COND_MSG(p_port < 1 || p_port > 65535, "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_MSG(!ip.is_valid(), "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_MSG(!ip.is_ipv4(), "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;
|
||||
|
||||
ENetBuffer enet_buffers[1];
|
||||
enet_buffers[0].data = (void *)p_packet.ptr();
|
||||
enet_buffers[0].dataLength = p_packet.size();
|
||||
|
||||
enet_socket_send(host->socket, &address, enet_buffers, 1);
|
||||
}
|
||||
|
||||
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", "server_options"), &ENetConnection::dtls_server_setup);
|
||||
ClassDB::bind_method(D_METHOD("dtls_client_setup", "hostname", "client_options"), &ENetConnection::dtls_client_setup, DEFVAL(Ref<TLSOptions>()));
|
||||
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);
|
||||
ClassDB::bind_method(D_METHOD("socket_send", "destination_address", "destination_port", "packet"), &ENetConnection::socket_send);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
size_t total = inLimit;
|
||||
size_t ofs = 0;
|
||||
while (total) {
|
||||
for (size_t i = 0; i < inBufferCount; i++) {
|
||||
const size_t to_copy = MIN(total, 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));
|
||||
}
|
||||
}
|
||||
|
||||
const int64_t req_size = Compression::get_max_compressed_buffer_size(ofs, mode);
|
||||
if (compressor->dst_mem.size() < req_size) {
|
||||
compressor->dst_mem.resize(req_size);
|
||||
}
|
||||
const int64_t ret = Compression::compress(compressor->dst_mem.ptrw(), compressor->src_mem.ptr(), ofs, mode);
|
||||
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t ret_size = size_t(ret);
|
||||
if (ret_size > outLimit) {
|
||||
return 0; // Do not bother
|
||||
}
|
||||
|
||||
memcpy(outData, compressor->dst_mem.ptr(), ret_size);
|
||||
|
||||
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);
|
||||
int64_t 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_NULL(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;
|
||||
}
|
141
modules/enet/enet_connection.h
Normal file
141
modules/enet/enet_connection.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/**************************************************************************/
|
||||
/* enet_connection.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "enet_packet_peer.h"
|
||||
|
||||
#include "core/crypto/crypto.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
template <typename T>
|
||||
class TypedArray;
|
||||
|
||||
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;
|
||||
|
||||
EventType _parse_event(const ENetEvent &p_event, Event &r_event);
|
||||
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);
|
||||
TypedArray<ENetPacketPeer> _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);
|
||||
void socket_send(const String &p_address, int p_port, const PackedByteArray &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);
|
||||
int check_events(EventType &r_type, 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(const Ref<TLSOptions> &p_options);
|
||||
Error dtls_client_setup(const String &p_hostname, const Ref<TLSOptions> &p_options);
|
||||
void refuse_new_connections(bool p_refuse);
|
||||
|
||||
ENetConnection() {}
|
||||
~ENetConnection();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(ENetConnection::CompressionMode);
|
||||
VARIANT_ENUM_CAST(ENetConnection::EventType);
|
||||
VARIANT_ENUM_CAST(ENetConnection::HostStatistic);
|
499
modules/enet/enet_multiplayer_peer.cpp
Normal file
499
modules/enet/enet_multiplayer_peer.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
/**************************************************************************/
|
||||
/* enet_multiplayer_peer.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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_multiplayer_peer.h"
|
||||
|
||||
void ENetMultiplayerPeer::set_target_peer(int p_peer) {
|
||||
target_peer = p_peer;
|
||||
}
|
||||
|
||||
int ENetMultiplayerPeer::get_packet_peer() const {
|
||||
ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active.");
|
||||
ERR_FAIL_COND_V(incoming_packets.is_empty(), 1);
|
||||
|
||||
return incoming_packets.front()->get().from;
|
||||
}
|
||||
|
||||
MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_packet_mode() const {
|
||||
ERR_FAIL_COND_V_MSG(!_is_active(), TRANSFER_MODE_RELIABLE, "The multiplayer instance isn't currently active.");
|
||||
ERR_FAIL_COND_V(incoming_packets.is_empty(), TRANSFER_MODE_RELIABLE);
|
||||
return incoming_packets.front()->get().transfer_mode;
|
||||
}
|
||||
|
||||
int ENetMultiplayerPeer::get_packet_channel() const {
|
||||
ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active.");
|
||||
ERR_FAIL_COND_V(incoming_packets.is_empty(), 1);
|
||||
int ch = incoming_packets.front()->get().channel;
|
||||
if (ch >= SYSCH_MAX) { // First 2 channels are reserved.
|
||||
return ch - SYSCH_MAX + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.");
|
||||
set_refuse_new_connections(false);
|
||||
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;
|
||||
}
|
||||
|
||||
active_mode = MODE_SERVER;
|
||||
unique_id = 1;
|
||||
connection_status = CONNECTION_CONNECTED;
|
||||
hosts[0] = host;
|
||||
return OK;
|
||||
}
|
||||
|
||||
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.");
|
||||
set_refuse_new_connections(false);
|
||||
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 {
|
||||
err = host->create_host(1, 0, p_in_bandwidth, p_out_bandwidth);
|
||||
}
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
unique_id = generate_unique_id();
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
// Need to wait for CONNECT event.
|
||||
connection_status = CONNECTION_CONNECTING;
|
||||
active_mode = MODE_CLIENT;
|
||||
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;
|
||||
unique_id = p_id;
|
||||
connection_status = CONNECTION_CONNECTED;
|
||||
return OK;
|
||||
}
|
||||
|
||||
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.front()->get()->get_state() != ENetPacketPeer::STATE_CONNECTED, ERR_INVALID_PARAMETER, "The provided host must have exactly one peer in the connected state.");
|
||||
hosts[p_id] = p_host;
|
||||
peers[p_id] = host_peers.front()->get();
|
||||
emit_signal(SNAME("peer_connected"), p_id);
|
||||
return OK;
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_store_packet(int32_t p_source, ENetConnection::Event &p_event) {
|
||||
Packet packet;
|
||||
packet.packet = p_event.packet;
|
||||
packet.channel = p_event.channel_id;
|
||||
packet.from = p_source;
|
||||
if (p_event.packet->flags & ENET_PACKET_FLAG_RELIABLE) {
|
||||
packet.transfer_mode = TRANSFER_MODE_RELIABLE;
|
||||
} else if (p_event.packet->flags & ENET_PACKET_FLAG_UNSEQUENCED) {
|
||||
packet.transfer_mode = TRANSFER_MODE_UNRELIABLE;
|
||||
} else {
|
||||
packet.transfer_mode = TRANSFER_MODE_UNRELIABLE_ORDERED;
|
||||
}
|
||||
packet.packet->referenceCount++;
|
||||
incoming_packets.push_back(packet);
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_disconnect_inactive_peers() {
|
||||
HashSet<int> to_drop;
|
||||
for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
|
||||
if (E.value->is_active()) {
|
||||
continue;
|
||||
}
|
||||
to_drop.insert(E.key);
|
||||
}
|
||||
for (const int &P : to_drop) {
|
||||
peers.erase(P);
|
||||
if (hosts.has(P)) {
|
||||
hosts.erase(P);
|
||||
}
|
||||
ERR_CONTINUE(active_mode == MODE_CLIENT && P != TARGET_PEER_SERVER);
|
||||
emit_signal(SNAME("peer_disconnected"), P);
|
||||
}
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::poll() {
|
||||
ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active.");
|
||||
|
||||
_pop_current_packet();
|
||||
|
||||
_disconnect_inactive_peers();
|
||||
|
||||
switch (active_mode) {
|
||||
case MODE_CLIENT: {
|
||||
if (!peers.has(1)) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
ENetConnection::Event event;
|
||||
ENetConnection::EventType ret = hosts[0]->service(0, event);
|
||||
do {
|
||||
if (ret == ENetConnection::EVENT_CONNECT) {
|
||||
connection_status = CONNECTION_CONNECTED;
|
||||
emit_signal(SNAME("peer_connected"), 1);
|
||||
} else if (ret == ENetConnection::EVENT_DISCONNECT) {
|
||||
if (connection_status == CONNECTION_CONNECTED) {
|
||||
// Client just disconnected from server.
|
||||
emit_signal(SNAME("peer_disconnected"), 1);
|
||||
}
|
||||
close();
|
||||
} else if (ret == ENetConnection::EVENT_RECEIVE) {
|
||||
_store_packet(1, event);
|
||||
} else if (ret != ENetConnection::EVENT_NONE) {
|
||||
close(); // Error.
|
||||
}
|
||||
} while (hosts.has(0) && hosts[0]->check_events(ret, event) > 0);
|
||||
} break;
|
||||
case MODE_SERVER: {
|
||||
ENetConnection::Event event;
|
||||
ENetConnection::EventType ret = hosts[0]->service(0, event);
|
||||
do {
|
||||
if (ret == ENetConnection::EVENT_CONNECT) {
|
||||
if (is_refusing_new_connections()) {
|
||||
event.peer->reset();
|
||||
continue;
|
||||
}
|
||||
// Client joined with invalid ID, probably trying to exploit us.
|
||||
if (event.data < 2 || peers.has((int)event.data)) {
|
||||
event.peer->reset();
|
||||
continue;
|
||||
}
|
||||
int id = event.data;
|
||||
event.peer->set_meta(SNAME("_net_id"), id);
|
||||
peers[id] = event.peer;
|
||||
emit_signal(SNAME("peer_connected"), id);
|
||||
} else if (ret == ENetConnection::EVENT_DISCONNECT) {
|
||||
int id = event.peer->get_meta(SNAME("_net_id"));
|
||||
if (!peers.has(id)) {
|
||||
// Never fully connected.
|
||||
continue;
|
||||
}
|
||||
emit_signal(SNAME("peer_disconnected"), id);
|
||||
peers.erase(id);
|
||||
} else if (ret == ENetConnection::EVENT_RECEIVE) {
|
||||
int32_t source = event.peer->get_meta(SNAME("_net_id"));
|
||||
_store_packet(source, event);
|
||||
} else if (ret != ENetConnection::EVENT_NONE) {
|
||||
close(); // Error
|
||||
}
|
||||
} while (hosts.has(0) && hosts[0]->check_events(ret, event) > 0);
|
||||
} break;
|
||||
case MODE_MESH: {
|
||||
HashSet<int> to_drop;
|
||||
for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
|
||||
ENetConnection::Event event;
|
||||
ENetConnection::EventType ret = E.value->service(0, event);
|
||||
do {
|
||||
if (ret == ENetConnection::EVENT_CONNECT) {
|
||||
event.peer->reset();
|
||||
} else if (ret == ENetConnection::EVENT_RECEIVE) {
|
||||
_store_packet(E.key, event);
|
||||
} else if (ret == ENetConnection::EVENT_NONE) {
|
||||
break; // Keep polling the others.
|
||||
} else {
|
||||
to_drop.insert(E.key); // Error or disconnect.
|
||||
break; // Keep polling the others.
|
||||
}
|
||||
} while (E.value->check_events(ret, event) > 0);
|
||||
}
|
||||
for (const int &P : to_drop) {
|
||||
if (peers.has(P)) {
|
||||
emit_signal(SNAME("peer_disconnected"), P);
|
||||
peers.erase(P);
|
||||
}
|
||||
hosts.erase(P);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool ENetMultiplayerPeer::is_server() const {
|
||||
return active_mode == MODE_SERVER;
|
||||
}
|
||||
|
||||
bool ENetMultiplayerPeer::is_server_relay_supported() const {
|
||||
return active_mode == MODE_SERVER || active_mode == MODE_CLIENT;
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::disconnect_peer(int p_peer, bool p_force) {
|
||||
ERR_FAIL_COND(!_is_active() || !peers.has(p_peer));
|
||||
peers[p_peer]->peer_disconnect(0); // Will be removed during next poll.
|
||||
if (active_mode == MODE_CLIENT || active_mode == MODE_SERVER) {
|
||||
hosts[0]->flush();
|
||||
} else {
|
||||
ERR_FAIL_COND(!hosts.has(p_peer));
|
||||
hosts[p_peer]->flush();
|
||||
}
|
||||
if (p_force) {
|
||||
peers.erase(p_peer);
|
||||
if (hosts.has(p_peer)) {
|
||||
hosts.erase(p_peer);
|
||||
}
|
||||
if (active_mode == MODE_CLIENT) {
|
||||
hosts.clear(); // Avoid flushing again.
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::close() {
|
||||
if (!_is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_pop_current_packet();
|
||||
|
||||
for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
|
||||
if (E.value.is_valid() && E.value->get_state() == ENetPacketPeer::STATE_CONNECTED) {
|
||||
E.value->peer_disconnect_now(0);
|
||||
}
|
||||
}
|
||||
for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
|
||||
E.value->flush();
|
||||
E.value->destroy();
|
||||
}
|
||||
|
||||
active_mode = MODE_NONE;
|
||||
incoming_packets.clear();
|
||||
peers.clear();
|
||||
hosts.clear();
|
||||
unique_id = 0;
|
||||
connection_status = CONNECTION_DISCONNECTED;
|
||||
set_refuse_new_connections(false);
|
||||
}
|
||||
|
||||
int ENetMultiplayerPeer::get_available_packet_count() const {
|
||||
return incoming_packets.size();
|
||||
}
|
||||
|
||||
Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
|
||||
ERR_FAIL_COND_V_MSG(incoming_packets.is_empty(), ERR_UNAVAILABLE, "No incoming packets available.");
|
||||
|
||||
_pop_current_packet();
|
||||
|
||||
current_packet = incoming_packets.front()->get();
|
||||
incoming_packets.pop_front();
|
||||
|
||||
*r_buffer = (const uint8_t *)(current_packet.packet->data);
|
||||
r_buffer_size = current_packet.packet->dataLength;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
|
||||
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(Math::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;
|
||||
int tr_channel = get_transfer_channel();
|
||||
switch (get_transfer_mode()) {
|
||||
case TRANSFER_MODE_UNRELIABLE: {
|
||||
packet_flags = ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
|
||||
channel = SYSCH_UNRELIABLE;
|
||||
} break;
|
||||
case TRANSFER_MODE_UNRELIABLE_ORDERED: {
|
||||
packet_flags = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
|
||||
channel = SYSCH_UNRELIABLE;
|
||||
} break;
|
||||
case TRANSFER_MODE_RELIABLE: {
|
||||
packet_flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
channel = SYSCH_RELIABLE;
|
||||
} break;
|
||||
}
|
||||
if (tr_channel > 0) {
|
||||
channel = SYSCH_MAX + tr_channel - 1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size > ENET_HOST_DEFAULT_MTU) {
|
||||
WARN_PRINT_ONCE(vformat("Sending %d bytes unreliably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size, ENET_HOST_DEFAULT_MTU));
|
||||
}
|
||||
#endif
|
||||
|
||||
ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size, packet_flags);
|
||||
memcpy(&packet->data[0], p_buffer, p_buffer_size);
|
||||
|
||||
if (is_server()) {
|
||||
if (target_peer == 0) {
|
||||
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 (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();
|
||||
|
||||
} 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();
|
||||
|
||||
} else {
|
||||
if (target_peer <= 0) {
|
||||
int exclude = Math::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();
|
||||
}
|
||||
_destroy_unused(packet);
|
||||
} else {
|
||||
peers[target_peer]->send(channel, packet);
|
||||
ERR_FAIL_COND_V(!hosts.has(target_peer), ERR_BUG);
|
||||
hosts[target_peer]->flush();
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int ENetMultiplayerPeer::get_max_packet_size() const {
|
||||
return 1 << 24; // Anything is good
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_pop_current_packet() {
|
||||
if (current_packet.packet) {
|
||||
current_packet.packet->referenceCount--;
|
||||
_destroy_unused(current_packet.packet);
|
||||
current_packet.packet = nullptr;
|
||||
current_packet.from = 0;
|
||||
current_packet.channel = -1;
|
||||
}
|
||||
}
|
||||
|
||||
MultiplayerPeer::ConnectionStatus ENetMultiplayerPeer::get_connection_status() const {
|
||||
return connection_status;
|
||||
}
|
||||
|
||||
int ENetMultiplayerPeer::get_unique_id() const {
|
||||
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_enabled) {
|
||||
#ifdef GODOT_ENET
|
||||
if (_is_active()) {
|
||||
for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
|
||||
E.value->refuse_new_connections(p_enabled);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MultiplayerPeer::set_refuse_new_connections(p_enabled);
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_destroy_unused(ENetPacket *p_packet) {
|
||||
if (p_packet->referenceCount == 0) {
|
||||
enet_packet_destroy(p_packet);
|
||||
}
|
||||
}
|
||||
|
||||
void ENetMultiplayerPeer::_bind_methods() {
|
||||
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("set_bind_ip", "ip"), &ENetMultiplayerPeer::set_bind_ip);
|
||||
|
||||
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::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host");
|
||||
}
|
||||
|
||||
ENetMultiplayerPeer::ENetMultiplayerPeer() {
|
||||
bind_ip = IPAddress("*");
|
||||
}
|
||||
|
||||
ENetMultiplayerPeer::~ENetMultiplayerPeer() {
|
||||
if (_is_active()) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
// Sets IP for ENet to bind when using create_server or create_client
|
||||
// if no IP is set, then ENet bind to ENET_HOST_ANY
|
||||
void ENetMultiplayerPeer::set_bind_ip(const IPAddress &p_ip) {
|
||||
ERR_FAIL_COND_MSG(!p_ip.is_valid() && !p_ip.is_wildcard(), vformat("Invalid bind IP address: %s", String(p_ip)));
|
||||
|
||||
bind_ip = p_ip;
|
||||
}
|
133
modules/enet/enet_multiplayer_peer.h
Normal file
133
modules/enet/enet_multiplayer_peer.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/**************************************************************************/
|
||||
/* enet_multiplayer_peer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "enet_connection.h"
|
||||
|
||||
#include "core/crypto/crypto.h"
|
||||
#include "scene/main/multiplayer_peer.h"
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
class ENetMultiplayerPeer : public MultiplayerPeer {
|
||||
GDCLASS(ENetMultiplayerPeer, MultiplayerPeer);
|
||||
|
||||
private:
|
||||
enum {
|
||||
SYSMSG_ADD_PEER,
|
||||
SYSMSG_REMOVE_PEER
|
||||
};
|
||||
|
||||
enum {
|
||||
SYSCH_RELIABLE = 0,
|
||||
SYSCH_UNRELIABLE = 1,
|
||||
SYSCH_MAX = 2
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
MODE_NONE,
|
||||
MODE_SERVER,
|
||||
MODE_CLIENT,
|
||||
MODE_MESH,
|
||||
};
|
||||
|
||||
Mode active_mode = MODE_NONE;
|
||||
|
||||
uint32_t unique_id = 0;
|
||||
|
||||
int target_peer = 0;
|
||||
|
||||
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
|
||||
|
||||
HashMap<int, Ref<ENetConnection>> hosts;
|
||||
HashMap<int, Ref<ENetPacketPeer>> peers;
|
||||
|
||||
struct Packet {
|
||||
ENetPacket *packet = nullptr;
|
||||
int from = 0;
|
||||
int channel = 0;
|
||||
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
|
||||
};
|
||||
|
||||
List<Packet> incoming_packets;
|
||||
|
||||
Packet current_packet;
|
||||
|
||||
void _store_packet(int32_t p_source, ENetConnection::Event &p_event);
|
||||
void _pop_current_packet();
|
||||
void _disconnect_inactive_peers();
|
||||
void _destroy_unused(ENetPacket *p_packet);
|
||||
_FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; }
|
||||
|
||||
IPAddress bind_ip;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void set_target_peer(int p_peer) override;
|
||||
|
||||
virtual int get_packet_peer() const override;
|
||||
virtual TransferMode get_packet_mode() const override;
|
||||
virtual int get_packet_channel() const override;
|
||||
|
||||
virtual void poll() override;
|
||||
virtual void close() override;
|
||||
virtual void disconnect_peer(int p_peer, bool p_force = false) override;
|
||||
|
||||
virtual bool is_server() const override;
|
||||
virtual bool is_server_relay_supported() const override;
|
||||
|
||||
// Overridden so we can instrument the DTLSServer when needed.
|
||||
virtual void set_refuse_new_connections(bool p_enabled) override;
|
||||
|
||||
virtual ConnectionStatus get_connection_status() const override;
|
||||
|
||||
virtual int get_unique_id() const override;
|
||||
|
||||
virtual int get_max_packet_size() const override;
|
||||
virtual int get_available_packet_count() const override;
|
||||
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
|
||||
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
|
||||
|
||||
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 set_bind_ip(const IPAddress &p_ip);
|
||||
|
||||
Ref<ENetConnection> get_host() const;
|
||||
Ref<ENetPacketPeer> get_peer(int p_id) const;
|
||||
|
||||
ENetMultiplayerPeer();
|
||||
~ENetMultiplayerPeer();
|
||||
};
|
271
modules/enet/enet_packet_peer.cpp
Normal file
271
modules/enet/enet_packet_peer.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/**************************************************************************/
|
||||
/* enet_packet_peer.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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_NULL(peer);
|
||||
enet_peer_disconnect(peer, p_data);
|
||||
}
|
||||
|
||||
void ENetPacketPeer::peer_disconnect_later(int p_data) {
|
||||
ERR_FAIL_NULL(peer);
|
||||
enet_peer_disconnect_later(peer, p_data);
|
||||
}
|
||||
|
||||
void ENetPacketPeer::peer_disconnect_now(int p_data) {
|
||||
ERR_FAIL_NULL(peer);
|
||||
enet_peer_disconnect_now(peer, p_data);
|
||||
_on_disconnect();
|
||||
}
|
||||
|
||||
void ENetPacketPeer::ping() {
|
||||
ERR_FAIL_NULL(peer);
|
||||
enet_peer_ping(peer);
|
||||
}
|
||||
|
||||
void ENetPacketPeer::ping_interval(int p_interval) {
|
||||
ERR_FAIL_NULL(peer);
|
||||
enet_peer_ping_interval(peer, p_interval);
|
||||
}
|
||||
|
||||
int ENetPacketPeer::send(uint8_t p_channel, ENetPacket *p_packet) {
|
||||
ERR_FAIL_NULL_V(peer, -1);
|
||||
ERR_FAIL_NULL_V(p_packet, -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_NULL_MSG(peer, "Peer not connected.");
|
||||
enet_peer_reset(peer);
|
||||
_on_disconnect();
|
||||
}
|
||||
|
||||
void ENetPacketPeer::throttle_configure(int p_interval, int p_acceleration, int p_deceleration) {
|
||||
ERR_FAIL_NULL_MSG(peer, "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_NULL_MSG(peer, "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 than 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_NULL_V(peer, ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V(packet_queue.is_empty(), 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_NULL_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_NULL_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_NULL_V(peer, 0);
|
||||
return peer->address.port;
|
||||
}
|
||||
|
||||
bool ENetPacketPeer::is_active() const {
|
||||
return peer != nullptr;
|
||||
}
|
||||
|
||||
double ENetPacketPeer::get_statistic(PeerStatistic p_stat) {
|
||||
ERR_FAIL_NULL_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_NULL_V_MSG(peer, 0, "The ENetConnection instance isn't currently active.");
|
||||
return peer->channelCount;
|
||||
}
|
||||
|
||||
int ENetPacketPeer::get_packet_flags() const {
|
||||
ERR_FAIL_COND_V(packet_queue.is_empty(), 0);
|
||||
return packet_queue.front()->get()->flags;
|
||||
}
|
||||
|
||||
void ENetPacketPeer::_on_disconnect() {
|
||||
if (peer) {
|
||||
peer->data = nullptr;
|
||||
}
|
||||
peer = nullptr;
|
||||
}
|
||||
|
||||
void ENetPacketPeer::_queue_packet(ENetPacket *p_packet) {
|
||||
ERR_FAIL_NULL(peer);
|
||||
packet_queue.push_back(p_packet);
|
||||
}
|
||||
|
||||
Error ENetPacketPeer::_send(int p_channel, PackedByteArray p_packet, int p_flags) {
|
||||
ERR_FAIL_NULL_V_MSG(peer, 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_packet_flags"), &ENetPacketPeer::get_packet_flags);
|
||||
ClassDB::bind_method(D_METHOD("get_remote_address"), &ENetPacketPeer::get_remote_address);
|
||||
ClassDB::bind_method(D_METHOD("get_remote_port"), &ENetPacketPeer::get_remote_port);
|
||||
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();
|
||||
}
|
129
modules/enet/enet_packet_peer.h
Normal file
129
modules/enet/enet_packet_peer.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/**************************************************************************/
|
||||
/* enet_packet_peer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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;
|
||||
int get_packet_flags() 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);
|
65
modules/enet/register_types.cpp
Normal file
65
modules/enet/register_types.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/**************************************************************************/
|
||||
/* register_types.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "register_types.h"
|
||||
|
||||
#include "enet_connection.h"
|
||||
#include "enet_multiplayer_peer.h"
|
||||
#include "enet_packet_peer.h"
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
|
||||
static bool enet_ok = false;
|
||||
|
||||
void initialize_enet_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enet_initialize() != 0) {
|
||||
ERR_PRINT("ENet initialization failure");
|
||||
} else {
|
||||
enet_ok = true;
|
||||
}
|
||||
|
||||
GDREGISTER_CLASS(ENetMultiplayerPeer);
|
||||
GDREGISTER_ABSTRACT_CLASS(ENetPacketPeer);
|
||||
GDREGISTER_CLASS(ENetConnection);
|
||||
}
|
||||
|
||||
void uninitialize_enet_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (enet_ok) {
|
||||
enet_deinitialize();
|
||||
}
|
||||
}
|
36
modules/enet/register_types.h
Normal file
36
modules/enet/register_types.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/**************************************************************************/
|
||||
/* register_types.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "modules/register_module_types.h"
|
||||
|
||||
void initialize_enet_module(ModuleInitializationLevel p_level);
|
||||
void uninitialize_enet_module(ModuleInitializationLevel p_level);
|
Reference in New Issue
Block a user