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

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

13
modules/webrtc/SCsub Normal file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
env_webrtc = env_modules.Clone()
if env["platform"] == "web":
# Our JavaScript/C++ interface.
env.AddJSLibraries(["library_godot_webrtc.js"])
env_webrtc.add_source_files(env.modules_sources, "*.cpp")

20
modules/webrtc/config.py Normal file
View File

@@ -0,0 +1,20 @@
def can_build(env, platform):
return True
def configure(env):
pass
def get_doc_classes():
return [
"WebRTCPeerConnection",
"WebRTCDataChannel",
"WebRTCMultiplayerPeer",
"WebRTCPeerConnectionExtension",
"WebRTCDataChannelExtension",
]
def get_doc_path():
return "doc_classes"

View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebRTCDataChannel" inherits="PacketPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="close">
<return type="void" />
<description>
Closes this data channel, notifying the other peer.
</description>
</method>
<method name="get_buffered_amount" qualifiers="const">
<return type="int" />
<description>
Returns the number of bytes currently queued to be sent over this channel.
</description>
</method>
<method name="get_id" qualifiers="const">
<return type="int" />
<description>
Returns the ID assigned to this channel during creation (or auto-assigned during negotiation).
If the channel is not negotiated out-of-band the ID will only be available after the connection is established (will return [code]65535[/code] until then).
</description>
</method>
<method name="get_label" qualifiers="const">
<return type="String" />
<description>
Returns the label assigned to this channel during creation.
</description>
</method>
<method name="get_max_packet_life_time" qualifiers="const">
<return type="int" />
<description>
Returns the [code]maxPacketLifeTime[/code] value assigned to this channel during creation.
Will be [code]65535[/code] if not specified.
</description>
</method>
<method name="get_max_retransmits" qualifiers="const">
<return type="int" />
<description>
Returns the [code]maxRetransmits[/code] value assigned to this channel during creation.
Will be [code]65535[/code] if not specified.
</description>
</method>
<method name="get_protocol" qualifiers="const">
<return type="String" />
<description>
Returns the sub-protocol assigned to this channel during creation. An empty string if not specified.
</description>
</method>
<method name="get_ready_state" qualifiers="const">
<return type="int" enum="WebRTCDataChannel.ChannelState" />
<description>
Returns the current state of this channel.
</description>
</method>
<method name="is_negotiated" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this channel was created with out-of-band configuration.
</description>
</method>
<method name="is_ordered" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this channel was created with ordering enabled (default).
</description>
</method>
<method name="poll">
<return type="int" enum="Error" />
<description>
Reserved, but not used for now.
</description>
</method>
<method name="was_string_packet" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if the last received packet was transferred as text. See [member write_mode].
</description>
</method>
</methods>
<members>
<member name="write_mode" type="int" setter="set_write_mode" getter="get_write_mode" enum="WebRTCDataChannel.WriteMode" default="1">
The transfer mode to use when sending outgoing packet. Either text or binary.
</member>
</members>
<constants>
<constant name="WRITE_MODE_TEXT" value="0" enum="WriteMode">
Tells the channel to send data over this channel as text. An external peer (non-Godot) would receive this as a string.
</constant>
<constant name="WRITE_MODE_BINARY" value="1" enum="WriteMode">
Tells the channel to send data over this channel as binary. An external peer (non-Godot) would receive this as array buffer or blob.
</constant>
<constant name="STATE_CONNECTING" value="0" enum="ChannelState">
The channel was created, but it's still trying to connect.
</constant>
<constant name="STATE_OPEN" value="1" enum="ChannelState">
The channel is currently open, and data can flow over it.
</constant>
<constant name="STATE_CLOSING" value="2" enum="ChannelState">
The channel is being closed, no new messages will be accepted, but those already in queue will be flushed.
</constant>
<constant name="STATE_CLOSED" value="3" enum="ChannelState">
The channel was closed, or connection failed.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebRTCDataChannelExtension" inherits="WebRTCDataChannel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="_close" qualifiers="virtual required">
<return type="void" />
<description>
</description>
</method>
<method name="_get_available_packet_count" qualifiers="virtual required const">
<return type="int" />
<description>
</description>
</method>
<method name="_get_buffered_amount" qualifiers="virtual required const">
<return type="int" />
<description>
</description>
</method>
<method name="_get_id" qualifiers="virtual required const">
<return type="int" />
<description>
</description>
</method>
<method name="_get_label" qualifiers="virtual required const">
<return type="String" />
<description>
</description>
</method>
<method name="_get_max_packet_life_time" qualifiers="virtual required const">
<return type="int" />
<description>
</description>
</method>
<method name="_get_max_packet_size" qualifiers="virtual required const">
<return type="int" />
<description>
</description>
</method>
<method name="_get_max_retransmits" qualifiers="virtual required const">
<return type="int" />
<description>
</description>
</method>
<method name="_get_packet" qualifiers="virtual">
<return type="int" enum="Error" />
<param index="0" name="r_buffer" type="const uint8_t **" />
<param index="1" name="r_buffer_size" type="int32_t*" />
<description>
</description>
</method>
<method name="_get_protocol" qualifiers="virtual required const">
<return type="String" />
<description>
</description>
</method>
<method name="_get_ready_state" qualifiers="virtual required const">
<return type="int" enum="WebRTCDataChannel.ChannelState" />
<description>
</description>
</method>
<method name="_get_write_mode" qualifiers="virtual required const">
<return type="int" enum="WebRTCDataChannel.WriteMode" />
<description>
</description>
</method>
<method name="_is_negotiated" qualifiers="virtual required const">
<return type="bool" />
<description>
</description>
</method>
<method name="_is_ordered" qualifiers="virtual required const">
<return type="bool" />
<description>
</description>
</method>
<method name="_poll" qualifiers="virtual required">
<return type="int" enum="Error" />
<description>
</description>
</method>
<method name="_put_packet" qualifiers="virtual">
<return type="int" enum="Error" />
<param index="0" name="p_buffer" type="const uint8_t*" />
<param index="1" name="p_buffer_size" type="int" />
<description>
</description>
</method>
<method name="_set_write_mode" qualifiers="virtual required">
<return type="void" />
<param index="0" name="p_write_mode" type="int" enum="WebRTCDataChannel.WriteMode" />
<description>
</description>
</method>
<method name="_was_string_packet" qualifiers="virtual required const">
<return type="bool" />
<description>
</description>
</method>
</methods>
</class>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebRTCMultiplayerPeer" inherits="MultiplayerPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI].
</brief_description>
<description>
This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_peer].
You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections.
When creating the peer via [method create_client] or [method create_server] the [method MultiplayerPeer.is_server_relay_supported] method will return [code]true[/code] enabling peer exchange and packet relaying when supported by the [MultiplayerAPI] implementation.
[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>
</tutorials>
<methods>
<method name="add_peer">
<return type="int" enum="Error" />
<param index="0" name="peer" type="WebRTCPeerConnection" />
<param index="1" name="peer_id" type="int" />
<param index="2" name="unreliable_lifetime" type="int" default="1" />
<description>
Add a new peer to the mesh with the given [param peer_id]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW].
Three channels will be created for reliable, unreliable, and ordered transport. The value of [param unreliable_lifetime] will be passed to the [code]"maxPacketLifetime"[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]).
</description>
</method>
<method name="create_client">
<return type="int" enum="Error" />
<param index="0" name="peer_id" type="int" />
<param index="1" name="channels_config" type="Array" default="[]" />
<description>
Initialize the multiplayer peer as a client with the given [param peer_id] (must be between 2 and 2147483647). In this mode, you should only call [method add_peer] once and with [param peer_id] of [code]1[/code]. This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
You can optionally specify a [param channels_config] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="create_mesh">
<return type="int" enum="Error" />
<param index="0" name="peer_id" type="int" />
<param index="1" name="channels_config" type="Array" default="[]" />
<description>
Initialize the multiplayer peer as a mesh (i.e. all peers connect to each other) with the given [param peer_id] (must be between 1 and 2147483647).
</description>
</method>
<method name="create_server">
<return type="int" enum="Error" />
<param index="0" name="channels_config" type="Array" default="[]" />
<description>
Initialize the multiplayer peer as a server (with unique ID of [code]1[/code]). This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying.
You can optionally specify a [param channels_config] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="get_peer">
<return type="Dictionary" />
<param index="0" name="peer_id" type="int" />
<description>
Returns a dictionary representation of the peer with given [param peer_id] with three keys. [code]"connection"[/code] containing the [WebRTCPeerConnection] to this peer, [code]"channels"[/code] an array of three [WebRTCDataChannel], and [code]"connected"[/code] a boolean representing if the peer connection is currently connected (all three channels are open).
</description>
</method>
<method name="get_peers">
<return type="Dictionary" />
<description>
Returns a dictionary which keys are the peer ids and values the peer representation as in [method get_peer].
</description>
</method>
<method name="has_peer">
<return type="bool" />
<param index="0" name="peer_id" type="int" />
<description>
Returns [code]true[/code] if the given [param peer_id] is in the peers map (it might not be connected though).
</description>
</method>
<method name="remove_peer">
<return type="void" />
<param index="0" name="peer_id" type="int" />
<description>
Remove the peer with given [param peer_id] from the mesh. If the peer was connected, and [signal MultiplayerPeer.peer_connected] was emitted for it, then [signal MultiplayerPeer.peer_disconnected] will be emitted.
</description>
</method>
</methods>
</class>

View File

@@ -0,0 +1,208 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebRTCPeerConnection" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Interface to a WebRTC peer connection.
</brief_description>
<description>
A WebRTC connection between the local computer and a remote peer. Provides an interface to connect, maintain and monitor the connection.
Setting up a WebRTC connection between two peers may not seem a trivial task, but it can be broken down into 3 main steps:
- The peer that wants to initiate the connection ([code]A[/code] from now on) creates an offer and send it to the other peer ([code]B[/code] from now on).
- [code]B[/code] receives the offer, generate and answer, and sends it to [code]A[/code]).
- [code]A[/code] and [code]B[/code] then generates and exchange ICE candidates with each other.
After these steps, the connection should become connected. Keep on reading or look into the tutorial for more information.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_ice_candidate">
<return type="int" enum="Error" />
<param index="0" name="media" type="String" />
<param index="1" name="index" type="int" />
<param index="2" name="name" type="String" />
<description>
Add an ice candidate generated by a remote peer (and received over the signaling server). See [signal ice_candidate_created].
</description>
</method>
<method name="close">
<return type="void" />
<description>
Close the peer connection and all data channels associated with it.
[b]Note:[/b] You cannot reuse this object for a new connection unless you call [method initialize].
</description>
</method>
<method name="create_data_channel">
<return type="WebRTCDataChannel" />
<param index="0" name="label" type="String" />
<param index="1" name="options" type="Dictionary" default="{}" />
<description>
Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [param label] and optionally configured via the [param options] dictionary. This method can only be called when the connection is in state [constant STATE_NEW].
There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]"negotiated"[/code] option set to [code]true[/code].
Valid [param options] are:
[codeblock]
{
"negotiated": true, # When set to true (default off), means the channel is negotiated out of band. "id" must be set too. "data_channel_received" will not be called.
"id": 1, # When "negotiated" is true this value must also be set to the same value on both peer.
# Only one of maxRetransmits and maxPacketLifeTime can be specified, not both. They make the channel unreliable (but also better at real time).
"maxRetransmits": 1, # Specify the maximum number of attempt the peer will make to retransmits packets if they are not acknowledged.
"maxPacketLifeTime": 100, # Specify the maximum amount of time before giving up retransmitions of unacknowledged packets (in milliseconds).
"ordered": true, # When in unreliable mode (i.e. either "maxRetransmits" or "maxPacketLifetime" is set), "ordered" (true by default) specify if packet ordering is to be enforced.
"protocol": "my-custom-protocol", # A custom sub-protocol string for this channel.
}
[/codeblock]
[b]Note:[/b] You must keep a reference to channels created this way, or it will be closed.
</description>
</method>
<method name="create_offer">
<return type="int" enum="Error" />
<description>
Creates a new SDP offer to start a WebRTC connection with a remote peer. At least one [WebRTCDataChannel] must have been created before calling this method.
If this functions returns [constant OK], [signal session_description_created] will be called when the session is ready to be sent.
</description>
</method>
<method name="get_connection_state" qualifiers="const">
<return type="int" enum="WebRTCPeerConnection.ConnectionState" />
<description>
Returns the connection state.
</description>
</method>
<method name="get_gathering_state" qualifiers="const">
<return type="int" enum="WebRTCPeerConnection.GatheringState" />
<description>
Returns the ICE [enum GatheringState] of the connection. This lets you detect, for example, when collection of ICE candidates has finished.
</description>
</method>
<method name="get_signaling_state" qualifiers="const">
<return type="int" enum="WebRTCPeerConnection.SignalingState" />
<description>
Returns the signaling state on the local end of the connection while connecting or reconnecting to another peer.
</description>
</method>
<method name="initialize">
<return type="int" enum="Error" />
<param index="0" name="configuration" type="Dictionary" default="{}" />
<description>
Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [param configuration] options can be passed to configure the peer connection.
Valid [param configuration] options are:
[codeblock]
{
"iceServers": [
{
"urls": [ "stun:stun.example.com:3478" ], # One or more STUN servers.
},
{
"urls": [ "turn:turn.example.com:3478" ], # One or more TURN servers.
"username": "a_username", # Optional username for the TURN server.
"credential": "a_password", # Optional password for the TURN server.
}
]
}
[/codeblock]
</description>
</method>
<method name="poll">
<return type="int" enum="Error" />
<description>
Call this method frequently (e.g. in [method Node._process] or [method Node._physics_process]) to properly receive signals.
</description>
</method>
<method name="set_default_extension" qualifiers="static">
<return type="void" />
<param index="0" name="extension_class" type="StringName" />
<description>
Sets the [param extension_class] as the default [WebRTCPeerConnectionExtension] returned when creating a new [WebRTCPeerConnection].
</description>
</method>
<method name="set_local_description">
<return type="int" enum="Error" />
<param index="0" name="type" type="String" />
<param index="1" name="sdp" type="String" />
<description>
Sets the SDP description of the local peer. This should be called in response to [signal session_description_created].
After calling this function the peer will start emitting [signal ice_candidate_created] (unless an [enum Error] different from [constant OK] is returned).
</description>
</method>
<method name="set_remote_description">
<return type="int" enum="Error" />
<param index="0" name="type" type="String" />
<param index="1" name="sdp" type="String" />
<description>
Sets the SDP description of the remote peer. This should be called with the values generated by a remote peer and received over the signaling server.
If [param type] is [code]"offer"[/code] the peer will emit [signal session_description_created] with the appropriate answer.
If [param type] is [code]"answer"[/code] the peer will start emitting [signal ice_candidate_created].
</description>
</method>
</methods>
<signals>
<signal name="data_channel_received">
<param index="0" name="channel" type="WebRTCDataChannel" />
<description>
Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default).
The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel].
</description>
</signal>
<signal name="ice_candidate_created">
<param index="0" name="media" type="String" />
<param index="1" name="index" type="int" />
<param index="2" name="name" type="String" />
<description>
Emitted when a new ICE candidate has been created. The three parameters are meant to be passed to the remote peer over the signaling server.
</description>
</signal>
<signal name="session_description_created">
<param index="0" name="type" type="String" />
<param index="1" name="sdp" type="String" />
<description>
Emitted after a successful call to [method create_offer] or [method set_remote_description] (when it generates an answer). The parameters are meant to be passed to [method set_local_description] on this object, and sent to the remote peer over the signaling server.
</description>
</signal>
</signals>
<constants>
<constant name="STATE_NEW" value="0" enum="ConnectionState">
The connection is new, data channels and an offer can be created in this state.
</constant>
<constant name="STATE_CONNECTING" value="1" enum="ConnectionState">
The peer is connecting, ICE is in progress, none of the transports has failed.
</constant>
<constant name="STATE_CONNECTED" value="2" enum="ConnectionState">
The peer is connected, all ICE transports are connected.
</constant>
<constant name="STATE_DISCONNECTED" value="3" enum="ConnectionState">
At least one ICE transport is disconnected.
</constant>
<constant name="STATE_FAILED" value="4" enum="ConnectionState">
One or more of the ICE transports failed.
</constant>
<constant name="STATE_CLOSED" value="5" enum="ConnectionState">
The peer connection is closed (after calling [method close] for example).
</constant>
<constant name="GATHERING_STATE_NEW" value="0" enum="GatheringState">
The peer connection was just created and hasn't done any networking yet.
</constant>
<constant name="GATHERING_STATE_GATHERING" value="1" enum="GatheringState">
The ICE agent is in the process of gathering candidates for the connection.
</constant>
<constant name="GATHERING_STATE_COMPLETE" value="2" enum="GatheringState">
The ICE agent has finished gathering candidates. If something happens that requires collecting new candidates, such as a new interface being added or the addition of a new ICE server, the state will revert to gathering to gather those candidates.
</constant>
<constant name="SIGNALING_STATE_STABLE" value="0" enum="SignalingState">
There is no ongoing exchange of offer and answer underway. This may mean that the [WebRTCPeerConnection] is new ([constant STATE_NEW]) or that negotiation is complete and a connection has been established ([constant STATE_CONNECTED]).
</constant>
<constant name="SIGNALING_STATE_HAVE_LOCAL_OFFER" value="1" enum="SignalingState">
The local peer has called [method set_local_description], passing in SDP representing an offer (usually created by calling [method create_offer]), and the offer has been applied successfully.
</constant>
<constant name="SIGNALING_STATE_HAVE_REMOTE_OFFER" value="2" enum="SignalingState">
The remote peer has created an offer and used the signaling server to deliver it to the local peer, which has set the offer as the remote description by calling [method set_remote_description].
</constant>
<constant name="SIGNALING_STATE_HAVE_LOCAL_PRANSWER" value="3" enum="SignalingState">
The offer sent by the remote peer has been applied and an answer has been created and applied by calling [method set_local_description]. This provisional answer describes the supported media formats and so forth, but may not have a complete set of ICE candidates included. Further candidates will be delivered separately later.
</constant>
<constant name="SIGNALING_STATE_HAVE_REMOTE_PRANSWER" value="4" enum="SignalingState">
A provisional answer has been received and successfully applied in response to an offer previously sent and established by calling [method set_local_description].
</constant>
<constant name="SIGNALING_STATE_CLOSED" value="5" enum="SignalingState">
The [WebRTCPeerConnection] has been closed.
</constant>
</constants>
</class>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="WebRTCPeerConnectionExtension" inherits="WebRTCPeerConnection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
<methods>
<method name="_add_ice_candidate" qualifiers="virtual required">
<return type="int" enum="Error" />
<param index="0" name="p_sdp_mid_name" type="String" />
<param index="1" name="p_sdp_mline_index" type="int" />
<param index="2" name="p_sdp_name" type="String" />
<description>
</description>
</method>
<method name="_close" qualifiers="virtual required">
<return type="void" />
<description>
</description>
</method>
<method name="_create_data_channel" qualifiers="virtual required">
<return type="WebRTCDataChannel" />
<param index="0" name="p_label" type="String" />
<param index="1" name="p_config" type="Dictionary" />
<description>
</description>
</method>
<method name="_create_offer" qualifiers="virtual required">
<return type="int" enum="Error" />
<description>
</description>
</method>
<method name="_get_connection_state" qualifiers="virtual required const">
<return type="int" enum="WebRTCPeerConnection.ConnectionState" />
<description>
</description>
</method>
<method name="_get_gathering_state" qualifiers="virtual required const">
<return type="int" enum="WebRTCPeerConnection.GatheringState" />
<description>
</description>
</method>
<method name="_get_signaling_state" qualifiers="virtual required const">
<return type="int" enum="WebRTCPeerConnection.SignalingState" />
<description>
</description>
</method>
<method name="_initialize" qualifiers="virtual required">
<return type="int" enum="Error" />
<param index="0" name="p_config" type="Dictionary" />
<description>
</description>
</method>
<method name="_poll" qualifiers="virtual required">
<return type="int" enum="Error" />
<description>
</description>
</method>
<method name="_set_local_description" qualifiers="virtual required">
<return type="int" enum="Error" />
<param index="0" name="p_type" type="String" />
<param index="1" name="p_sdp" type="String" />
<description>
</description>
</method>
<method name="_set_remote_description" qualifiers="virtual required">
<return type="int" enum="Error" />
<param index="0" name="p_type" type="String" />
<param index="1" name="p_sdp" type="String" />
<description>
</description>
</method>
</methods>
</class>

View File

@@ -0,0 +1,502 @@
/**************************************************************************/
/* library_godot_webrtc.js */
/**************************************************************************/
/* 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. */
/**************************************************************************/
const GodotRTCDataChannel = {
// Our socket implementation that forwards events to C++.
$GodotRTCDataChannel__deps: ['$IDHandler', '$GodotRuntime'],
$GodotRTCDataChannel: {
connect: function (p_id, p_on_open, p_on_message, p_on_error, p_on_close) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
ref.binaryType = 'arraybuffer';
ref.onopen = function (event) {
p_on_open();
};
ref.onclose = function (event) {
p_on_close();
};
ref.onerror = function (event) {
p_on_error();
};
ref.onmessage = function (event) {
let buffer;
let is_string = 0;
if (event.data instanceof ArrayBuffer) {
buffer = new Uint8Array(event.data);
} else if (event.data instanceof Blob) {
GodotRuntime.error('Blob type not supported');
return;
} else if (typeof event.data === 'string') {
is_string = 1;
const enc = new TextEncoder('utf-8');
buffer = new Uint8Array(enc.encode(event.data));
} else {
GodotRuntime.error('Unknown message type');
return;
}
const len = buffer.length * buffer.BYTES_PER_ELEMENT;
const out = GodotRuntime.malloc(len);
HEAPU8.set(buffer, out);
p_on_message(out, len, is_string);
GodotRuntime.free(out);
};
},
close: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
ref.onopen = null;
ref.onmessage = null;
ref.onerror = null;
ref.onclose = null;
ref.close();
},
get_prop: function (p_id, p_prop, p_def) {
const ref = IDHandler.get(p_id);
return (ref && ref[p_prop] !== undefined) ? ref[p_prop] : p_def;
},
},
godot_js_rtc_datachannel_ready_state_get__proxy: 'sync',
godot_js_rtc_datachannel_ready_state_get__sig: 'ii',
godot_js_rtc_datachannel_ready_state_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
return 3; // CLOSED
}
switch (ref.readyState) {
case 'connecting':
return 0;
case 'open':
return 1;
case 'closing':
return 2;
case 'closed':
default:
return 3;
}
},
godot_js_rtc_datachannel_send__proxy: 'sync',
godot_js_rtc_datachannel_send__sig: 'iiiii',
godot_js_rtc_datachannel_send: function (p_id, p_buffer, p_length, p_raw) {
const ref = IDHandler.get(p_id);
if (!ref) {
return 1;
}
const bytes_array = new Uint8Array(p_length);
for (let i = 0; i < p_length; i++) {
bytes_array[i] = GodotRuntime.getHeapValue(p_buffer + i, 'i8');
}
if (p_raw) {
ref.send(bytes_array.buffer);
} else {
const string = new TextDecoder('utf-8').decode(bytes_array);
ref.send(string);
}
return 0;
},
godot_js_rtc_datachannel_is_ordered__proxy: 'sync',
godot_js_rtc_datachannel_is_ordered__sig: 'ii',
godot_js_rtc_datachannel_is_ordered: function (p_id) {
return GodotRTCDataChannel.get_prop(p_id, 'ordered', true);
},
godot_js_rtc_datachannel_id_get__proxy: 'sync',
godot_js_rtc_datachannel_id_get__sig: 'ii',
godot_js_rtc_datachannel_id_get: function (p_id) {
return GodotRTCDataChannel.get_prop(p_id, 'id', 65535);
},
godot_js_rtc_datachannel_max_packet_lifetime_get__proxy: 'sync',
godot_js_rtc_datachannel_max_packet_lifetime_get__sig: 'ii',
godot_js_rtc_datachannel_max_packet_lifetime_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
return 65535;
}
if (ref['maxPacketLifeTime'] !== undefined) {
return ref['maxPacketLifeTime'];
} else if (ref['maxRetransmitTime'] !== undefined) {
// Guess someone didn't appreciate the standardization process.
return ref['maxRetransmitTime'];
}
return 65535;
},
godot_js_rtc_datachannel_max_retransmits_get__proxy: 'sync',
godot_js_rtc_datachannel_max_retransmits_get__sig: 'ii',
godot_js_rtc_datachannel_max_retransmits_get: function (p_id) {
return GodotRTCDataChannel.get_prop(p_id, 'maxRetransmits', 65535);
},
godot_js_rtc_datachannel_is_negotiated__proxy: 'sync',
godot_js_rtc_datachannel_is_negotiated__sig: 'ii',
godot_js_rtc_datachannel_is_negotiated: function (p_id) {
return GodotRTCDataChannel.get_prop(p_id, 'negotiated', 65535);
},
godot_js_rtc_datachannel_get_buffered_amount__proxy: 'sync',
godot_js_rtc_datachannel_get_buffered_amount__sig: 'ii',
godot_js_rtc_datachannel_get_buffered_amount: function (p_id) {
return GodotRTCDataChannel.get_prop(p_id, 'bufferedAmount', 0);
},
godot_js_rtc_datachannel_label_get__proxy: 'sync',
godot_js_rtc_datachannel_label_get__sig: 'ii',
godot_js_rtc_datachannel_label_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref || !ref.label) {
return 0;
}
return GodotRuntime.allocString(ref.label);
},
godot_js_rtc_datachannel_protocol_get__sig: 'ii',
godot_js_rtc_datachannel_protocol_get: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref || !ref.protocol) {
return 0;
}
return GodotRuntime.allocString(ref.protocol);
},
godot_js_rtc_datachannel_destroy__proxy: 'sync',
godot_js_rtc_datachannel_destroy__sig: 'vi',
godot_js_rtc_datachannel_destroy: function (p_id) {
GodotRTCDataChannel.close(p_id);
IDHandler.remove(p_id);
},
godot_js_rtc_datachannel_connect__proxy: 'sync',
godot_js_rtc_datachannel_connect__sig: 'viiiiii',
godot_js_rtc_datachannel_connect: function (p_id, p_ref, p_on_open, p_on_message, p_on_error, p_on_close) {
const onopen = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
const onmessage = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_ref);
const onclose = GodotRuntime.get_func(p_on_close).bind(null, p_ref);
GodotRTCDataChannel.connect(p_id, onopen, onmessage, onerror, onclose);
},
godot_js_rtc_datachannel_close__proxy: 'sync',
godot_js_rtc_datachannel_close__sig: 'vi',
godot_js_rtc_datachannel_close: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
GodotRTCDataChannel.close(p_id);
},
};
autoAddDeps(GodotRTCDataChannel, '$GodotRTCDataChannel');
mergeInto(LibraryManager.library, GodotRTCDataChannel);
const GodotRTCPeerConnection = {
$GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
$GodotRTCPeerConnection: {
// Enums
ConnectionState: {
'new': 0,
'connecting': 1,
'connected': 2,
'disconnected': 3,
'failed': 4,
'closed': 5,
},
ConnectionStateCompat: {
// Using values from IceConnectionState for browsers that do not support ConnectionState (notably Firefox).
'new': 0,
'checking': 1,
'connected': 2,
'completed': 2,
'disconnected': 3,
'failed': 4,
'closed': 5,
},
IceGatheringState: {
'new': 0,
'gathering': 1,
'complete': 2,
},
SignalingState: {
'stable': 0,
'have-local-offer': 1,
'have-remote-offer': 2,
'have-local-pranswer': 3,
'have-remote-pranswer': 4,
'closed': 5,
},
// Callbacks
create: function (config, onConnectionChange, onSignalingChange, onIceGatheringChange, onIceCandidate, onDataChannel) {
let conn = null;
try {
conn = new RTCPeerConnection(config);
} catch (e) {
GodotRuntime.error(e);
return 0;
}
const id = IDHandler.add(conn);
if ('connectionState' in conn && conn['connectionState'] !== undefined) {
// Use "connectionState" if supported
conn.onconnectionstatechange = function (event) {
if (!IDHandler.get(id)) {
return;
}
onConnectionChange(GodotRTCPeerConnection.ConnectionState[conn.connectionState] || 0);
};
} else {
// Fall back to using "iceConnectionState" when "connectionState" is not supported (notably Firefox).
conn.oniceconnectionstatechange = function (event) {
if (!IDHandler.get(id)) {
return;
}
onConnectionChange(GodotRTCPeerConnection.ConnectionStateCompat[conn.iceConnectionState] || 0);
};
}
conn.onicegatheringstatechange = function (event) {
if (!IDHandler.get(id)) {
return;
}
onIceGatheringChange(GodotRTCPeerConnection.IceGatheringState[conn.iceGatheringState] || 0);
};
conn.onsignalingstatechange = function (event) {
if (!IDHandler.get(id)) {
return;
}
onSignalingChange(GodotRTCPeerConnection.SignalingState[conn.signalingState] || 0);
};
conn.onicecandidate = function (event) {
if (!IDHandler.get(id)) {
return;
}
const c = event.candidate;
if (!c || !c.candidate) {
return;
}
const candidate_str = GodotRuntime.allocString(c.candidate);
const mid_str = GodotRuntime.allocString(c.sdpMid);
onIceCandidate(mid_str, c.sdpMLineIndex, candidate_str);
GodotRuntime.free(candidate_str);
GodotRuntime.free(mid_str);
};
conn.ondatachannel = function (event) {
if (!IDHandler.get(id)) {
return;
}
const cid = IDHandler.add(event.channel);
onDataChannel(cid);
};
return id;
},
destroy: function (p_id) {
const conn = IDHandler.get(p_id);
if (!conn) {
return;
}
conn.onconnectionstatechange = null;
conn.oniceconnectionstatechange = null;
conn.onicegatheringstatechange = null;
conn.onsignalingstatechange = null;
conn.onicecandidate = null;
conn.ondatachannel = null;
IDHandler.remove(p_id);
},
onsession: function (p_id, callback, session) {
if (!IDHandler.get(p_id)) {
return;
}
const type_str = GodotRuntime.allocString(session.type);
const sdp_str = GodotRuntime.allocString(session.sdp);
callback(type_str, sdp_str);
GodotRuntime.free(type_str);
GodotRuntime.free(sdp_str);
},
onerror: function (p_id, callback, error) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
GodotRuntime.error(error);
callback();
},
},
godot_js_rtc_pc_create__proxy: 'sync',
godot_js_rtc_pc_create__sig: 'iiiiiiii',
godot_js_rtc_pc_create: function (p_config, p_ref, p_on_connection_state_change, p_on_ice_gathering_state_change, p_on_signaling_state_change, p_on_ice_candidate, p_on_datachannel) {
const wrap = function (p_func) {
return GodotRuntime.get_func(p_func).bind(null, p_ref);
};
return GodotRTCPeerConnection.create(
JSON.parse(GodotRuntime.parseString(p_config)),
wrap(p_on_connection_state_change),
wrap(p_on_signaling_state_change),
wrap(p_on_ice_gathering_state_change),
wrap(p_on_ice_candidate),
wrap(p_on_datachannel)
);
},
godot_js_rtc_pc_close__proxy: 'sync',
godot_js_rtc_pc_close__sig: 'vi',
godot_js_rtc_pc_close: function (p_id) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
ref.close();
},
godot_js_rtc_pc_destroy__proxy: 'sync',
godot_js_rtc_pc_destroy__sig: 'vi',
godot_js_rtc_pc_destroy: function (p_id) {
GodotRTCPeerConnection.destroy(p_id);
},
godot_js_rtc_pc_offer_create__proxy: 'sync',
godot_js_rtc_pc_offer_create__sig: 'viiii',
godot_js_rtc_pc_offer_create: function (p_id, p_obj, p_on_session, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
const onsession = GodotRuntime.get_func(p_on_session).bind(null, p_obj);
const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
ref.createOffer().then(function (session) {
GodotRTCPeerConnection.onsession(p_id, onsession, session);
}).catch(function (error) {
GodotRTCPeerConnection.onerror(p_id, onerror, error);
});
},
godot_js_rtc_pc_local_description_set__proxy: 'sync',
godot_js_rtc_pc_local_description_set__sig: 'viiiii',
godot_js_rtc_pc_local_description_set: function (p_id, p_type, p_sdp, p_obj, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
const type = GodotRuntime.parseString(p_type);
const sdp = GodotRuntime.parseString(p_sdp);
const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
ref.setLocalDescription({
'sdp': sdp,
'type': type,
}).catch(function (error) {
GodotRTCPeerConnection.onerror(p_id, onerror, error);
});
},
godot_js_rtc_pc_remote_description_set__proxy: 'sync',
godot_js_rtc_pc_remote_description_set__sig: 'viiiiii',
godot_js_rtc_pc_remote_description_set: function (p_id, p_type, p_sdp, p_obj, p_session_created, p_on_error) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
const type = GodotRuntime.parseString(p_type);
const sdp = GodotRuntime.parseString(p_sdp);
const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
const onsession = GodotRuntime.get_func(p_session_created).bind(null, p_obj);
ref.setRemoteDescription({
'sdp': sdp,
'type': type,
}).then(function () {
if (type !== 'offer') {
return Promise.resolve();
}
return ref.createAnswer().then(function (session) {
GodotRTCPeerConnection.onsession(p_id, onsession, session);
});
}).catch(function (error) {
GodotRTCPeerConnection.onerror(p_id, onerror, error);
});
},
godot_js_rtc_pc_ice_candidate_add__proxy: 'sync',
godot_js_rtc_pc_ice_candidate_add__sig: 'viiii',
godot_js_rtc_pc_ice_candidate_add: function (p_id, p_mid_name, p_mline_idx, p_sdp) {
const ref = IDHandler.get(p_id);
if (!ref) {
return;
}
const sdpMidName = GodotRuntime.parseString(p_mid_name);
const sdpName = GodotRuntime.parseString(p_sdp);
ref.addIceCandidate(new RTCIceCandidate({
'candidate': sdpName,
'sdpMid': sdpMidName,
'sdpMlineIndex': p_mline_idx,
}));
},
godot_js_rtc_pc_datachannel_create__deps: ['$GodotRTCDataChannel'],
godot_js_rtc_pc_datachannel_create__proxy: 'sync',
godot_js_rtc_pc_datachannel_create__sig: 'iiii',
godot_js_rtc_pc_datachannel_create: function (p_id, p_label, p_config) {
try {
const ref = IDHandler.get(p_id);
if (!ref) {
return 0;
}
const label = GodotRuntime.parseString(p_label);
const config = JSON.parse(GodotRuntime.parseString(p_config));
const channel = ref.createDataChannel(label, config);
return IDHandler.add(channel);
} catch (e) {
GodotRuntime.error(e);
return 0;
}
},
};
autoAddDeps(GodotRTCPeerConnection, '$GodotRTCPeerConnection');
mergeInto(LibraryManager.library, GodotRTCPeerConnection);

View File

@@ -0,0 +1,60 @@
/**************************************************************************/
/* 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 "webrtc_data_channel.h"
#include "webrtc_data_channel_extension.h"
#include "webrtc_multiplayer_peer.h"
#include "webrtc_peer_connection.h"
#include "webrtc_peer_connection_extension.h"
#include "core/config/project_settings.h"
void initialize_webrtc_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
GLOBAL_DEF(PropertyInfo(Variant::INT, "network/limits/webrtc/max_channel_in_buffer_kb", PROPERTY_HINT_RANGE, "2,4096,1,or_greater"), 64);
ClassDB::register_custom_instance_class<WebRTCPeerConnection>();
GDREGISTER_CLASS(WebRTCPeerConnectionExtension);
GDREGISTER_ABSTRACT_CLASS(WebRTCDataChannel);
GDREGISTER_CLASS(WebRTCDataChannelExtension);
GDREGISTER_CLASS(WebRTCMultiplayerPeer);
}
void uninitialize_webrtc_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}

View 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_webrtc_module(ModuleInitializationLevel p_level);
void uninitialize_webrtc_module(ModuleInitializationLevel p_level);

View File

@@ -0,0 +1,68 @@
/**************************************************************************/
/* webrtc_data_channel.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 "webrtc_data_channel.h"
#include "core/config/project_settings.h"
void WebRTCDataChannel::_bind_methods() {
ClassDB::bind_method(D_METHOD("poll"), &WebRTCDataChannel::poll);
ClassDB::bind_method(D_METHOD("close"), &WebRTCDataChannel::close);
ClassDB::bind_method(D_METHOD("was_string_packet"), &WebRTCDataChannel::was_string_packet);
ClassDB::bind_method(D_METHOD("set_write_mode", "write_mode"), &WebRTCDataChannel::set_write_mode);
ClassDB::bind_method(D_METHOD("get_write_mode"), &WebRTCDataChannel::get_write_mode);
ClassDB::bind_method(D_METHOD("get_ready_state"), &WebRTCDataChannel::get_ready_state);
ClassDB::bind_method(D_METHOD("get_label"), &WebRTCDataChannel::get_label);
ClassDB::bind_method(D_METHOD("is_ordered"), &WebRTCDataChannel::is_ordered);
ClassDB::bind_method(D_METHOD("get_id"), &WebRTCDataChannel::get_id);
ClassDB::bind_method(D_METHOD("get_max_packet_life_time"), &WebRTCDataChannel::get_max_packet_life_time);
ClassDB::bind_method(D_METHOD("get_max_retransmits"), &WebRTCDataChannel::get_max_retransmits);
ClassDB::bind_method(D_METHOD("get_protocol"), &WebRTCDataChannel::get_protocol);
ClassDB::bind_method(D_METHOD("is_negotiated"), &WebRTCDataChannel::is_negotiated);
ClassDB::bind_method(D_METHOD("get_buffered_amount"), &WebRTCDataChannel::get_buffered_amount);
ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode");
BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
BIND_ENUM_CONSTANT(STATE_CONNECTING);
BIND_ENUM_CONSTANT(STATE_OPEN);
BIND_ENUM_CONSTANT(STATE_CLOSING);
BIND_ENUM_CONSTANT(STATE_CLOSED);
}
WebRTCDataChannel::WebRTCDataChannel() {
_in_buffer_shift = nearest_shift(uint32_t((int)GLOBAL_GET("network/limits/webrtc/max_channel_in_buffer_kb") - 1)) + (uint32_t)10;
}
WebRTCDataChannel::~WebRTCDataChannel() {
}

View File

@@ -0,0 +1,80 @@
/**************************************************************************/
/* webrtc_data_channel.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"
class WebRTCDataChannel : public PacketPeer {
GDCLASS(WebRTCDataChannel, PacketPeer);
public:
enum WriteMode {
WRITE_MODE_TEXT,
WRITE_MODE_BINARY,
};
enum ChannelState {
STATE_CONNECTING,
STATE_OPEN,
STATE_CLOSING,
STATE_CLOSED
};
protected:
unsigned int _in_buffer_shift;
static void _bind_methods();
public:
virtual void set_write_mode(WriteMode mode) = 0;
virtual WriteMode get_write_mode() const = 0;
virtual bool was_string_packet() const = 0;
virtual ChannelState get_ready_state() const = 0;
virtual String get_label() const = 0;
virtual bool is_ordered() const = 0;
virtual int get_id() const = 0;
virtual int get_max_packet_life_time() const = 0;
virtual int get_max_retransmits() const = 0;
virtual String get_protocol() const = 0;
virtual bool is_negotiated() const = 0;
virtual int get_buffered_amount() const = 0;
virtual Error poll() = 0;
virtual void close() = 0;
WebRTCDataChannel();
~WebRTCDataChannel();
};
VARIANT_ENUM_CAST(WebRTCDataChannel::WriteMode);
VARIANT_ENUM_CAST(WebRTCDataChannel::ChannelState);

View File

@@ -0,0 +1,75 @@
/**************************************************************************/
/* webrtc_data_channel_extension.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 "webrtc_data_channel_extension.h"
void WebRTCDataChannelExtension::_bind_methods() {
ADD_PROPERTY_DEFAULT("write_mode", WRITE_MODE_BINARY);
GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
GDVIRTUAL_BIND(_get_available_packet_count);
GDVIRTUAL_BIND(_get_max_packet_size);
GDVIRTUAL_BIND(_poll);
GDVIRTUAL_BIND(_close);
GDVIRTUAL_BIND(_set_write_mode, "p_write_mode");
GDVIRTUAL_BIND(_get_write_mode);
GDVIRTUAL_BIND(_was_string_packet);
GDVIRTUAL_BIND(_get_ready_state);
GDVIRTUAL_BIND(_get_label);
GDVIRTUAL_BIND(_is_ordered);
GDVIRTUAL_BIND(_get_id);
GDVIRTUAL_BIND(_get_max_packet_life_time);
GDVIRTUAL_BIND(_get_max_retransmits);
GDVIRTUAL_BIND(_get_protocol);
GDVIRTUAL_BIND(_is_negotiated);
GDVIRTUAL_BIND(_get_buffered_amount);
}
Error WebRTCDataChannelExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
Error err;
if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
return err;
}
WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_packet_native is unimplemented!");
return FAILED;
}
Error WebRTCDataChannelExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
Error err;
if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
return err;
}
WARN_PRINT_ONCE("WebRTCDataChannelExtension::_put_packet_native is unimplemented!");
return FAILED;
}

View File

@@ -0,0 +1,75 @@
/**************************************************************************/
/* webrtc_data_channel_extension.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 "webrtc_data_channel.h"
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.h"
class WebRTCDataChannelExtension : public WebRTCDataChannel {
GDCLASS(WebRTCDataChannelExtension, WebRTCDataChannel);
protected:
static void _bind_methods();
public:
EXBIND0R(Error, poll);
EXBIND0(close);
EXBIND1(set_write_mode, WriteMode);
EXBIND0RC(WriteMode, get_write_mode);
EXBIND0RC(bool, was_string_packet);
EXBIND0RC(ChannelState, get_ready_state);
EXBIND0RC(String, get_label);
EXBIND0RC(bool, is_ordered);
EXBIND0RC(int, get_id);
EXBIND0RC(int, get_max_packet_life_time);
EXBIND0RC(int, get_max_retransmits);
EXBIND0RC(String, get_protocol);
EXBIND0RC(bool, is_negotiated);
EXBIND0RC(int, get_buffered_amount);
/** Inherited from PacketPeer: **/
EXBIND0RC(int, get_available_packet_count);
EXBIND0RC(int, get_max_packet_size);
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
/** GDExtension **/
GDVIRTUAL2R(Error, _get_packet, GDExtensionConstPtr<const uint8_t *>, GDExtensionPtr<int>);
GDVIRTUAL2R(Error, _put_packet, GDExtensionConstPtr<const uint8_t>, int);
WebRTCDataChannelExtension() {}
};

View File

@@ -0,0 +1,217 @@
/**************************************************************************/
/* webrtc_data_channel_js.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 "webrtc_data_channel_js.h"
#ifdef WEB_ENABLED
#include <emscripten.h>
extern "C" {
typedef void (*RTCChOnOpen)(void *p_obj);
typedef void (*RTCChOnMessage)(void *p_obj, const uint8_t *p_buffer, int p_size, int p_is_string);
typedef void (*RTCChOnClose)(void *p_obj);
typedef void (*RTCChOnError)(void *p_obj);
extern int godot_js_rtc_datachannel_ready_state_get(int p_id);
extern int godot_js_rtc_datachannel_send(int p_id, const uint8_t *p_buffer, int p_length, int p_raw);
extern int godot_js_rtc_datachannel_is_ordered(int p_id);
extern int godot_js_rtc_datachannel_id_get(int p_id);
extern int godot_js_rtc_datachannel_max_packet_lifetime_get(int p_id);
extern int godot_js_rtc_datachannel_max_retransmits_get(int p_id);
extern int godot_js_rtc_datachannel_is_negotiated(int p_id);
extern int godot_js_rtc_datachannel_get_buffered_amount(int p_id);
extern char *godot_js_rtc_datachannel_label_get(int p_id); // Must free the returned string.
extern char *godot_js_rtc_datachannel_protocol_get(int p_id); // Must free the returned string.
extern void godot_js_rtc_datachannel_destroy(int p_id);
extern void godot_js_rtc_datachannel_connect(int p_id, void *p_obj, RTCChOnOpen p_on_open, RTCChOnMessage p_on_message, RTCChOnError p_on_error, RTCChOnClose p_on_close);
extern void godot_js_rtc_datachannel_close(int p_id);
}
void WebRTCDataChannelJS::_on_open(void *p_obj) {
WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
peer->in_buffer.resize(peer->_in_buffer_shift);
}
void WebRTCDataChannelJS::_on_close(void *p_obj) {
WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
peer->close();
}
void WebRTCDataChannelJS::_on_error(void *p_obj) {
WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
peer->close();
}
void WebRTCDataChannelJS::_on_message(void *p_obj, const uint8_t *p_data, int p_size, int p_is_string) {
WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
RingBuffer<uint8_t> &in_buffer = peer->in_buffer;
ERR_FAIL_COND_MSG(in_buffer.space_left() < (int)(p_size + 5), "Buffer full! Dropping data.");
uint8_t is_string = p_is_string ? 1 : 0;
in_buffer.write((uint8_t *)&p_size, 4);
in_buffer.write((uint8_t *)&is_string, 1);
in_buffer.write(p_data, p_size);
peer->queue_count++;
}
void WebRTCDataChannelJS::close() {
in_buffer.resize(0);
queue_count = 0;
_was_string = false;
godot_js_rtc_datachannel_close(_js_id);
}
Error WebRTCDataChannelJS::poll() {
return OK;
}
WebRTCDataChannelJS::ChannelState WebRTCDataChannelJS::get_ready_state() const {
return (ChannelState)godot_js_rtc_datachannel_ready_state_get(_js_id);
}
int WebRTCDataChannelJS::get_available_packet_count() const {
return queue_count;
}
Error WebRTCDataChannelJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED);
if (queue_count == 0) {
return ERR_UNAVAILABLE;
}
uint32_t to_read = 0;
uint32_t left = 0;
uint8_t is_string = 0;
r_buffer_size = 0;
in_buffer.read((uint8_t *)&to_read, 4);
--queue_count;
left = in_buffer.data_left();
if (left < to_read + 1) {
in_buffer.advance_read(left);
return FAILED;
}
in_buffer.read(&is_string, 1);
_was_string = is_string == 1;
in_buffer.read(packet_buffer, to_read);
*r_buffer = packet_buffer;
r_buffer_size = to_read;
return OK;
}
Error WebRTCDataChannelJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED);
int is_bin = _write_mode == WebRTCDataChannel::WRITE_MODE_BINARY ? 1 : 0;
godot_js_rtc_datachannel_send(_js_id, p_buffer, p_buffer_size, is_bin);
return OK;
}
int WebRTCDataChannelJS::get_max_packet_size() const {
return 1200;
}
void WebRTCDataChannelJS::set_write_mode(WriteMode p_mode) {
_write_mode = p_mode;
}
WebRTCDataChannel::WriteMode WebRTCDataChannelJS::get_write_mode() const {
return _write_mode;
}
bool WebRTCDataChannelJS::was_string_packet() const {
return _was_string;
}
String WebRTCDataChannelJS::get_label() const {
return _label;
}
bool WebRTCDataChannelJS::is_ordered() const {
return godot_js_rtc_datachannel_is_ordered(_js_id);
}
int WebRTCDataChannelJS::get_id() const {
return godot_js_rtc_datachannel_id_get(_js_id);
}
int WebRTCDataChannelJS::get_max_packet_life_time() const {
return godot_js_rtc_datachannel_max_packet_lifetime_get(_js_id);
}
int WebRTCDataChannelJS::get_max_retransmits() const {
return godot_js_rtc_datachannel_max_retransmits_get(_js_id);
}
String WebRTCDataChannelJS::get_protocol() const {
return _protocol;
}
bool WebRTCDataChannelJS::is_negotiated() const {
return godot_js_rtc_datachannel_is_negotiated(_js_id);
}
int WebRTCDataChannelJS::get_buffered_amount() const {
return godot_js_rtc_datachannel_get_buffered_amount(_js_id);
}
WebRTCDataChannelJS::WebRTCDataChannelJS() {
}
WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) {
_js_id = js_id;
godot_js_rtc_datachannel_connect(js_id, this, &_on_open, &_on_message, &_on_error, &_on_close);
// Parse label
char *label = godot_js_rtc_datachannel_label_get(js_id);
if (label) {
_label.clear();
_label.append_utf8(label);
free(label);
}
char *protocol = godot_js_rtc_datachannel_protocol_get(js_id);
if (protocol) {
_protocol.clear();
_protocol.append_utf8(protocol);
free(protocol);
}
}
WebRTCDataChannelJS::~WebRTCDataChannelJS() {
close();
godot_js_rtc_datachannel_destroy(_js_id);
}
#endif

View File

@@ -0,0 +1,91 @@
/**************************************************************************/
/* webrtc_data_channel_js.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
#ifdef WEB_ENABLED
#include "webrtc_data_channel.h"
class WebRTCDataChannelJS : public WebRTCDataChannel {
GDCLASS(WebRTCDataChannelJS, WebRTCDataChannel);
private:
String _label;
String _protocol;
bool _was_string = false;
WriteMode _write_mode = WRITE_MODE_BINARY;
enum {
PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
};
int _js_id = 0;
RingBuffer<uint8_t> in_buffer;
int queue_count = 0;
uint8_t packet_buffer[PACKET_BUFFER_SIZE];
static void _on_open(void *p_obj);
static void _on_close(void *p_obj);
static void _on_error(void *p_obj);
static void _on_message(void *p_obj, const uint8_t *p_data, int p_size, int p_is_string);
public:
virtual void set_write_mode(WriteMode mode) override;
virtual WriteMode get_write_mode() const override;
virtual bool was_string_packet() const override;
virtual ChannelState get_ready_state() const override;
virtual String get_label() const override;
virtual bool is_ordered() const override;
virtual int get_id() const override;
virtual int get_max_packet_life_time() const override;
virtual int get_max_retransmits() const override;
virtual String get_protocol() const override;
virtual bool is_negotiated() const override;
virtual int get_buffered_amount() const override;
virtual Error poll() override;
virtual void close() override;
/** Inherited from PacketPeer: **/
virtual int get_available_packet_count() const override;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
virtual int get_max_packet_size() const override;
WebRTCDataChannelJS();
WebRTCDataChannelJS(int js_id);
~WebRTCDataChannelJS();
};
#endif // WEB_ENABLED

View File

@@ -0,0 +1,453 @@
/**************************************************************************/
/* webrtc_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 "webrtc_multiplayer_peer.h"
void WebRTCMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_server", "channels_config"), &WebRTCMultiplayerPeer::create_server, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("create_client", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_client, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("create_mesh", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_mesh, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayerPeer::add_peer, DEFVAL(1));
ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayerPeer::remove_peer);
ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer);
ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayerPeer::get_peer);
ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayerPeer::get_peers);
}
void WebRTCMultiplayerPeer::set_target_peer(int p_peer_id) {
target_peer = p_peer_id;
}
/* Returns the ID of the MultiplayerPeer who sent the most recent packet: */
int WebRTCMultiplayerPeer::get_packet_peer() const {
return next_packet_peer;
}
int WebRTCMultiplayerPeer::get_packet_channel() const {
return next_packet_channel < CH_RESERVED_MAX ? 0 : next_packet_channel - CH_RESERVED_MAX + 1;
}
MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_packet_mode() const {
ERR_FAIL_INDEX_V(next_packet_channel, channels_modes.size(), TRANSFER_MODE_RELIABLE);
return channels_modes.get(next_packet_channel);
}
bool WebRTCMultiplayerPeer::is_server() const {
return unique_id == TARGET_PEER_SERVER;
}
void WebRTCMultiplayerPeer::poll() {
if (peer_map.is_empty()) {
return;
}
List<int> remove;
List<int> add;
for (KeyValue<int, Ref<ConnectedPeer>> &E : peer_map) {
Ref<ConnectedPeer> peer = E.value;
peer->connection->poll();
// Check peer state
switch (peer->connection->get_connection_state()) {
case WebRTCPeerConnection::STATE_NEW:
case WebRTCPeerConnection::STATE_CONNECTING:
// Go to next peer, not ready yet.
continue;
case WebRTCPeerConnection::STATE_CONNECTED:
// Good to go, go ahead and check channel state.
break;
default:
// Peer is closed or in error state. Got to next peer.
remove.push_back(E.key);
continue;
}
// Check channels state
int ready = 0;
for (List<Ref<WebRTCDataChannel>>::Element *C = peer->channels.front(); C && C->get().is_valid(); C = C->next()) {
Ref<WebRTCDataChannel> ch = C->get();
switch (ch->get_ready_state()) {
case WebRTCDataChannel::STATE_CONNECTING:
continue;
case WebRTCDataChannel::STATE_OPEN:
ready++;
continue;
default:
// Channel was closed or in error state, remove peer id.
remove.push_back(E.key);
}
// We got a closed channel break out, the peer will be removed.
break;
}
// This peer has newly connected, and all channels are now open.
if (ready == peer->channels.size() && !peer->connected) {
peer->connected = true;
add.push_back(E.key);
}
}
// Remove disconnected peers
for (int &E : remove) {
remove_peer(E);
if (next_packet_peer == E) {
next_packet_peer = 0;
}
}
// Signal newly connected peers
for (int &E : add) {
// Already connected to server: simply notify new peer.
if (network_mode == MODE_CLIENT) {
ERR_CONTINUE(E != TARGET_PEER_SERVER); // Bug.
// Server connected.
connection_status = CONNECTION_CONNECTED;
emit_signal(SNAME("peer_connected"), TARGET_PEER_SERVER);
} else {
emit_signal(SNAME("peer_connected"), E);
}
}
// Fetch next packet
if (next_packet_peer == 0) {
_find_next_peer();
}
}
void WebRTCMultiplayerPeer::_find_next_peer() {
HashMap<int, Ref<ConnectedPeer>>::Iterator E = peer_map.find(next_packet_peer);
if (E) {
++E;
}
// After last.
while (E) {
if (!E->value->connected) {
++E;
continue;
}
int idx = 0;
for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
next_packet_channel = idx;
next_packet_peer = E->key;
return;
}
idx++;
}
++E;
}
E = peer_map.begin();
// Before last
while (E) {
if (!E->value->connected) {
++E;
continue;
}
int idx = 0;
for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
next_packet_channel = idx;
next_packet_peer = E->key;
return;
}
idx++;
}
if (E->key == (int)next_packet_peer) {
break;
}
++E;
}
// No packet found
next_packet_channel = 0;
next_packet_peer = 0;
}
MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status() const {
return connection_status;
}
Error WebRTCMultiplayerPeer::create_server(Array p_channels_config) {
return _initialize(1, MODE_SERVER, p_channels_config);
}
Error WebRTCMultiplayerPeer::create_client(int p_self_id, Array p_channels_config) {
ERR_FAIL_COND_V_MSG(p_self_id == 1, ERR_INVALID_PARAMETER, "Clients cannot have ID 1.");
return _initialize(p_self_id, MODE_CLIENT, p_channels_config);
}
Error WebRTCMultiplayerPeer::create_mesh(int p_self_id, Array p_channels_config) {
return _initialize(p_self_id, MODE_MESH, p_channels_config);
}
Error WebRTCMultiplayerPeer::_initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config) {
ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
channels_config.clear();
channels_modes.clear();
channels_modes.push_back(TRANSFER_MODE_RELIABLE);
channels_modes.push_back(TRANSFER_MODE_UNRELIABLE_ORDERED);
channels_modes.push_back(TRANSFER_MODE_UNRELIABLE);
for (int i = 0; i < p_channels_config.size(); i++) {
ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'");
int mode = p_channels_config[i].operator int();
// Initialize data channel configurations.
Dictionary cfg;
cfg["id"] = CH_RESERVED_MAX + i + 1;
cfg["negotiated"] = true;
cfg["ordered"] = true;
switch (mode) {
case TRANSFER_MODE_UNRELIABLE_ORDERED:
cfg["maxPacketLifetime"] = 1;
break;
case TRANSFER_MODE_UNRELIABLE:
cfg["maxPacketLifetime"] = 1;
cfg["ordered"] = false;
break;
case TRANSFER_MODE_RELIABLE:
break;
default:
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'. Got: %d", mode));
}
channels_config.push_back(cfg);
channels_modes.push_back((TransferMode)mode);
}
unique_id = p_self_id;
network_mode = p_mode;
// Mesh and server are always connected
if (p_mode != MODE_CLIENT) {
connection_status = CONNECTION_CONNECTED;
} else {
connection_status = CONNECTION_CONNECTING;
}
return OK;
}
bool WebRTCMultiplayerPeer::is_server_relay_supported() const {
return network_mode == MODE_SERVER || network_mode == MODE_CLIENT;
}
int WebRTCMultiplayerPeer::get_unique_id() const {
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1);
return unique_id;
}
void WebRTCMultiplayerPeer::_peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict) {
Array channels;
for (Ref<WebRTCDataChannel> &F : p_connected_peer->channels) {
channels.push_back(F);
}
r_dict["connection"] = p_connected_peer->connection;
r_dict["connected"] = p_connected_peer->connected;
r_dict["channels"] = channels;
}
bool WebRTCMultiplayerPeer::has_peer(int p_peer_id) {
return peer_map.has(p_peer_id);
}
Dictionary WebRTCMultiplayerPeer::get_peer(int p_peer_id) {
ERR_FAIL_COND_V(!peer_map.has(p_peer_id), Dictionary());
Dictionary out;
_peer_to_dict(peer_map[p_peer_id], out);
return out;
}
Dictionary WebRTCMultiplayerPeer::get_peers() {
Dictionary out;
for (const KeyValue<int, Ref<ConnectedPeer>> &E : peer_map) {
Dictionary d;
_peer_to_dict(E.value, d);
out[E.key] = d;
}
return out;
}
Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) {
ERR_FAIL_COND_V(network_mode == MODE_NONE, ERR_UNCONFIGURED);
ERR_FAIL_COND_V(network_mode == MODE_CLIENT && p_peer_id != 1, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(network_mode == MODE_SERVER && p_peer_id == 1, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_peer_id < 1 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(is_refusing_new_connections(), ERR_UNAUTHORIZED);
// Peer must be valid, and in new state (to create data channels)
ERR_FAIL_COND_V(p_peer.is_null(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_peer->get_connection_state() != WebRTCPeerConnection::STATE_NEW, ERR_INVALID_PARAMETER);
Ref<ConnectedPeer> peer = memnew(ConnectedPeer);
peer->connection = p_peer;
// Initialize data channels
Dictionary cfg;
cfg["negotiated"] = true;
cfg["ordered"] = true;
cfg["id"] = 1;
peer->channels.get(CH_RELIABLE) = p_peer->create_data_channel("reliable", cfg);
ERR_FAIL_COND_V(peer->channels.get(CH_RELIABLE).is_null(), FAILED);
cfg["id"] = 2;
cfg["maxPacketLifetime"] = p_unreliable_lifetime;
peer->channels.get(CH_ORDERED) = p_peer->create_data_channel("ordered", cfg);
ERR_FAIL_COND_V(peer->channels.get(CH_ORDERED).is_null(), FAILED);
cfg["id"] = 3;
cfg["ordered"] = false;
peer->channels.get(CH_UNRELIABLE) = p_peer->create_data_channel("unreliable", cfg);
ERR_FAIL_COND_V(peer->channels.get(CH_UNRELIABLE).is_null(), FAILED);
for (const Dictionary &dict : channels_config) {
Ref<WebRTCDataChannel> ch = p_peer->create_data_channel(String::num_int64(dict["id"]), dict);
ERR_FAIL_COND_V(ch.is_null(), FAILED);
peer->channels.push_back(ch);
}
peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map
return OK;
}
void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) {
ERR_FAIL_COND(!peer_map.has(p_peer_id));
Ref<ConnectedPeer> peer = peer_map[p_peer_id];
peer_map.erase(p_peer_id);
if (peer->connected) {
peer->connected = false;
emit_signal(SNAME("peer_disconnected"), p_peer_id);
if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) {
connection_status = CONNECTION_DISCONNECTED;
}
}
}
void WebRTCMultiplayerPeer::disconnect_peer(int p_peer_id, bool p_force) {
ERR_FAIL_COND(!peer_map.has(p_peer_id));
if (p_force) {
peer_map.erase(p_peer_id);
if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) {
connection_status = CONNECTION_DISCONNECTED;
}
} else {
peer_map[p_peer_id]->connection->close(); // Will be removed during next poll.
}
}
Error WebRTCMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
// Peer not available
if (next_packet_peer == 0 || !peer_map.has(next_packet_peer)) {
_find_next_peer();
ERR_FAIL_V(ERR_UNAVAILABLE);
}
for (Ref<WebRTCDataChannel> &E : peer_map[next_packet_peer]->channels) {
if (E->get_available_packet_count()) {
Error err = E->get_packet(r_buffer, r_buffer_size);
_find_next_peer();
return err;
}
}
// Channels for that peer were empty. Bug?
_find_next_peer();
ERR_FAIL_V(ERR_BUG);
}
Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED);
int ch = get_transfer_channel();
if (ch == 0) {
switch (get_transfer_mode()) {
case TRANSFER_MODE_RELIABLE:
ch = CH_RELIABLE;
break;
case TRANSFER_MODE_UNRELIABLE_ORDERED:
ch = CH_ORDERED;
break;
case TRANSFER_MODE_UNRELIABLE:
ch = CH_UNRELIABLE;
break;
}
} else {
ch += CH_RESERVED_MAX - 1;
}
if (target_peer > 0) {
HashMap<int, Ref<ConnectedPeer>>::Iterator E = peer_map.find(target_peer);
ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + ".");
ERR_FAIL_COND_V_MSG(E->value->channels.size() <= ch, ERR_INVALID_PARAMETER, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value->channels.size()));
ERR_FAIL_COND_V(E->value->channels.get(ch).is_null(), ERR_BUG);
return E->value->channels.get(ch)->put_packet(p_buffer, p_buffer_size);
} else {
int exclude = -target_peer;
for (KeyValue<int, Ref<ConnectedPeer>> &F : peer_map) {
// Exclude packet. If target_peer == 0 then don't exclude any packets
if (target_peer != 0 && F.key == exclude) {
continue;
}
ERR_CONTINUE_MSG(F.value->channels.size() <= ch, vformat("Unable to send packet on channel %d, max channels: %d", ch, F.value->channels.size()));
ERR_CONTINUE(F.value->channels.get(ch).is_null());
F.value->channels.get(ch)->put_packet(p_buffer, p_buffer_size);
}
}
return OK;
}
int WebRTCMultiplayerPeer::get_available_packet_count() const {
if (next_packet_peer == 0) {
return 0; // To be sure next call to get_packet works if size > 0 .
}
int size = 0;
for (const KeyValue<int, Ref<ConnectedPeer>> &E : peer_map) {
if (!E.value->connected) {
continue;
}
for (const Ref<WebRTCDataChannel> &F : E.value->channels) {
size += F->get_available_packet_count();
}
}
return size;
}
int WebRTCMultiplayerPeer::get_max_packet_size() const {
return 1200;
}
void WebRTCMultiplayerPeer::close() {
peer_map.clear();
channels_config.clear();
unique_id = 0;
next_packet_peer = 0;
next_packet_channel = 0;
target_peer = 0;
network_mode = MODE_NONE;
connection_status = CONNECTION_DISCONNECTED;
}
WebRTCMultiplayerPeer::~WebRTCMultiplayerPeer() {
close();
}

View File

@@ -0,0 +1,124 @@
/**************************************************************************/
/* webrtc_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 "webrtc_peer_connection.h"
#include "scene/main/multiplayer_peer.h"
class WebRTCMultiplayerPeer : public MultiplayerPeer {
GDCLASS(WebRTCMultiplayerPeer, MultiplayerPeer);
protected:
static void _bind_methods();
private:
enum {
CH_RELIABLE = 0,
CH_ORDERED = 1,
CH_UNRELIABLE = 2,
CH_RESERVED_MAX = 3
};
enum NetworkMode {
MODE_NONE,
MODE_SERVER,
MODE_CLIENT,
MODE_MESH,
};
class ConnectedPeer : public RefCounted {
public:
Ref<WebRTCPeerConnection> connection;
List<Ref<WebRTCDataChannel>> channels;
bool connected;
ConnectedPeer() {
connected = false;
for (int i = 0; i < CH_RESERVED_MAX; i++) {
channels.push_front(Ref<WebRTCDataChannel>());
}
}
};
uint32_t unique_id = 0;
int target_peer = 0;
int client_count = 0;
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
int next_packet_peer = 0;
int next_packet_channel = 0;
NetworkMode network_mode = MODE_NONE;
HashMap<int, Ref<ConnectedPeer>> peer_map;
List<TransferMode> channels_modes;
List<Dictionary> channels_config;
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
void _find_next_peer();
Ref<ConnectedPeer> _get_next_peer();
Error _initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config = Array());
public:
WebRTCMultiplayerPeer() {}
~WebRTCMultiplayerPeer();
Error create_server(Array p_channels_config = Array());
Error create_client(int p_self_id, Array p_channels_config = Array());
Error create_mesh(int p_self_id, Array p_channels_config = Array());
Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1);
void remove_peer(int p_peer_id);
bool has_peer(int p_peer_id);
Dictionary get_peer(int p_peer_id);
Dictionary get_peers();
// PacketPeer
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
virtual int get_available_packet_count() const override;
virtual int get_max_packet_size() const override;
// MultiplayerPeer
virtual void set_target_peer(int p_peer_id) override;
virtual int get_unique_id() const override;
virtual int get_packet_peer() const override;
virtual int get_packet_channel() const override;
virtual TransferMode get_packet_mode() const override;
virtual bool is_server() const override;
virtual bool is_server_relay_supported() const override;
virtual void poll() override;
virtual void close() override;
virtual void disconnect_peer(int p_peer_id, bool p_force = false) override;
virtual ConnectionStatus get_connection_status() const override;
};

View File

@@ -0,0 +1,107 @@
/**************************************************************************/
/* webrtc_peer_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 "webrtc_peer_connection.h"
#ifdef WEB_ENABLED
#include "webrtc_peer_connection_js.h"
#endif
#include "webrtc_peer_connection_extension.h"
StringName WebRTCPeerConnection::default_extension;
void WebRTCPeerConnection::set_default_extension(const StringName &p_extension) {
ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_extension, WebRTCPeerConnectionExtension::get_class_static()), vformat("Can't make %s the default WebRTC extension since it does not extend WebRTCPeerConnectionExtension.", p_extension));
default_extension = StringName(p_extension, true);
}
WebRTCPeerConnection *WebRTCPeerConnection::create(bool p_notify_postinitialize) {
#ifdef WEB_ENABLED
return static_cast<WebRTCPeerConnection *>(ClassDB::creator<WebRTCPeerConnectionJS>(p_notify_postinitialize));
#else
if (default_extension == StringName()) {
WARN_PRINT_ONCE("No default WebRTC extension configured.");
return static_cast<WebRTCPeerConnection *>(ClassDB::creator<WebRTCPeerConnectionExtension>(p_notify_postinitialize));
}
Object *obj = nullptr;
if (p_notify_postinitialize) {
obj = ClassDB::instantiate(default_extension);
} else {
obj = ClassDB::instantiate_without_postinitialization(default_extension);
}
return Object::cast_to<WebRTCPeerConnectionExtension>(obj);
#endif
}
void WebRTCPeerConnection::_bind_methods() {
ClassDB::bind_static_method(get_class_static(), D_METHOD("set_default_extension", "extension_class"), &WebRTCPeerConnectionExtension::set_default_extension);
ClassDB::bind_method(D_METHOD("initialize", "configuration"), &WebRTCPeerConnection::initialize, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("create_data_channel", "label", "options"), &WebRTCPeerConnection::create_data_channel, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeerConnection::create_offer);
ClassDB::bind_method(D_METHOD("set_local_description", "type", "sdp"), &WebRTCPeerConnection::set_local_description);
ClassDB::bind_method(D_METHOD("set_remote_description", "type", "sdp"), &WebRTCPeerConnection::set_remote_description);
ClassDB::bind_method(D_METHOD("add_ice_candidate", "media", "index", "name"), &WebRTCPeerConnection::add_ice_candidate);
ClassDB::bind_method(D_METHOD("poll"), &WebRTCPeerConnection::poll);
ClassDB::bind_method(D_METHOD("close"), &WebRTCPeerConnection::close);
ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeerConnection::get_connection_state);
ClassDB::bind_method(D_METHOD("get_gathering_state"), &WebRTCPeerConnection::get_gathering_state);
ClassDB::bind_method(D_METHOD("get_signaling_state"), &WebRTCPeerConnection::get_signaling_state);
ADD_SIGNAL(MethodInfo("session_description_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp")));
ADD_SIGNAL(MethodInfo("ice_candidate_created", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name")));
ADD_SIGNAL(MethodInfo("data_channel_received", PropertyInfo(Variant::OBJECT, "channel", PROPERTY_HINT_RESOURCE_TYPE, "WebRTCDataChannel")));
BIND_ENUM_CONSTANT(STATE_NEW);
BIND_ENUM_CONSTANT(STATE_CONNECTING);
BIND_ENUM_CONSTANT(STATE_CONNECTED);
BIND_ENUM_CONSTANT(STATE_DISCONNECTED);
BIND_ENUM_CONSTANT(STATE_FAILED);
BIND_ENUM_CONSTANT(STATE_CLOSED);
BIND_ENUM_CONSTANT(GATHERING_STATE_NEW);
BIND_ENUM_CONSTANT(GATHERING_STATE_GATHERING);
BIND_ENUM_CONSTANT(GATHERING_STATE_COMPLETE);
BIND_ENUM_CONSTANT(SIGNALING_STATE_STABLE);
BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_LOCAL_OFFER);
BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_REMOTE_OFFER);
BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_LOCAL_PRANSWER);
BIND_ENUM_CONSTANT(SIGNALING_STATE_HAVE_REMOTE_PRANSWER);
BIND_ENUM_CONSTANT(SIGNALING_STATE_CLOSED);
}
WebRTCPeerConnection::WebRTCPeerConnection() {
}
WebRTCPeerConnection::~WebRTCPeerConnection() {
}

View File

@@ -0,0 +1,93 @@
/**************************************************************************/
/* webrtc_peer_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 "webrtc_data_channel.h"
class WebRTCPeerConnection : public RefCounted {
GDCLASS(WebRTCPeerConnection, RefCounted);
public:
enum ConnectionState {
STATE_NEW,
STATE_CONNECTING,
STATE_CONNECTED,
STATE_DISCONNECTED,
STATE_FAILED,
STATE_CLOSED
};
enum GatheringState {
GATHERING_STATE_NEW,
GATHERING_STATE_GATHERING,
GATHERING_STATE_COMPLETE,
};
enum SignalingState {
SIGNALING_STATE_STABLE,
SIGNALING_STATE_HAVE_LOCAL_OFFER,
SIGNALING_STATE_HAVE_REMOTE_OFFER,
SIGNALING_STATE_HAVE_LOCAL_PRANSWER,
SIGNALING_STATE_HAVE_REMOTE_PRANSWER,
SIGNALING_STATE_CLOSED,
};
private:
static StringName default_extension;
protected:
static void _bind_methods();
public:
static void set_default_extension(const StringName &p_name);
virtual ConnectionState get_connection_state() const = 0;
virtual GatheringState get_gathering_state() const = 0;
virtual SignalingState get_signaling_state() const = 0;
virtual Error initialize(Dictionary p_config = Dictionary()) = 0;
virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()) = 0;
virtual Error create_offer() = 0;
virtual Error set_remote_description(String type, String sdp) = 0;
virtual Error set_local_description(String type, String sdp) = 0;
virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) = 0;
virtual Error poll() = 0;
virtual void close() = 0;
static WebRTCPeerConnection *create(bool p_notify_postinitialize = true);
WebRTCPeerConnection();
~WebRTCPeerConnection();
};
VARIANT_ENUM_CAST(WebRTCPeerConnection::ConnectionState);
VARIANT_ENUM_CAST(WebRTCPeerConnection::GatheringState);
VARIANT_ENUM_CAST(WebRTCPeerConnection::SignalingState);

View File

@@ -0,0 +1,45 @@
/**************************************************************************/
/* webrtc_peer_connection_extension.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 "webrtc_peer_connection_extension.h"
void WebRTCPeerConnectionExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_connection_state);
GDVIRTUAL_BIND(_get_gathering_state);
GDVIRTUAL_BIND(_get_signaling_state);
GDVIRTUAL_BIND(_initialize, "p_config");
GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
GDVIRTUAL_BIND(_create_offer);
GDVIRTUAL_BIND(_set_remote_description, "p_type", "p_sdp");
GDVIRTUAL_BIND(_set_local_description, "p_type", "p_sdp");
GDVIRTUAL_BIND(_add_ice_candidate, "p_sdp_mid_name", "p_sdp_mline_index", "p_sdp_name");
GDVIRTUAL_BIND(_poll);
GDVIRTUAL_BIND(_close);
}

View File

@@ -0,0 +1,59 @@
/**************************************************************************/
/* webrtc_peer_connection_extension.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 "webrtc_peer_connection.h"
#include "core/extension/ext_wrappers.gen.inc"
#include "core/object/gdvirtual.gen.inc"
class WebRTCPeerConnectionExtension : public WebRTCPeerConnection {
GDCLASS(WebRTCPeerConnectionExtension, WebRTCPeerConnection);
protected:
static void _bind_methods();
public:
/** GDExtension **/
EXBIND0RC(ConnectionState, get_connection_state);
EXBIND0RC(GatheringState, get_gathering_state);
EXBIND0RC(SignalingState, get_signaling_state);
EXBIND1R(Error, initialize, Dictionary);
EXBIND2R(Ref<WebRTCDataChannel>, create_data_channel, String, Dictionary);
EXBIND0R(Error, create_offer);
EXBIND2R(Error, set_remote_description, String, String);
EXBIND2R(Error, set_local_description, String, String);
EXBIND3R(Error, add_ice_candidate, String, int, String);
EXBIND0R(Error, poll);
EXBIND0(close);
WebRTCPeerConnectionExtension() {}
};

View File

@@ -0,0 +1,154 @@
/**************************************************************************/
/* webrtc_peer_connection_js.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 "webrtc_peer_connection_js.h"
#ifdef WEB_ENABLED
#include "webrtc_data_channel_js.h"
#include <emscripten.h>
void WebRTCPeerConnectionJS::_on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate) {
WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
peer->emit_signal(SNAME("ice_candidate_created"), String(p_mid_name), p_mline_idx, String(p_candidate));
}
void WebRTCPeerConnectionJS::_on_session_created(void *p_obj, const char *p_type, const char *p_session) {
WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
peer->emit_signal(SNAME("session_description_created"), String(p_type), String(p_session));
}
void WebRTCPeerConnectionJS::_on_connection_state_changed(void *p_obj, int p_state) {
WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
peer->_conn_state = (ConnectionState)p_state;
}
void WebRTCPeerConnectionJS::_on_gathering_state_changed(void *p_obj, int p_state) {
WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
peer->_gathering_state = (GatheringState)p_state;
}
void WebRTCPeerConnectionJS::_on_signaling_state_changed(void *p_obj, int p_state) {
WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
peer->_signaling_state = (SignalingState)p_state;
}
void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
ERR_PRINT("RTCPeerConnection error!");
}
void WebRTCPeerConnectionJS::_on_data_channel(void *p_obj, int p_id) {
WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
peer->emit_signal(SNAME("data_channel_received"), Ref<WebRTCDataChannel>(memnew(WebRTCDataChannelJS(p_id))));
}
void WebRTCPeerConnectionJS::close() {
godot_js_rtc_pc_close(_js_id);
_conn_state = STATE_CLOSED;
}
Error WebRTCPeerConnectionJS::create_offer() {
ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
_conn_state = STATE_CONNECTING;
godot_js_rtc_pc_offer_create(_js_id, this, &_on_session_created, &_on_error);
return OK;
}
Error WebRTCPeerConnectionJS::set_local_description(String type, String sdp) {
godot_js_rtc_pc_local_description_set(_js_id, type.utf8().get_data(), sdp.utf8().get_data(), this, &_on_error);
return OK;
}
Error WebRTCPeerConnectionJS::set_remote_description(String type, String sdp) {
if (type == "offer") {
ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
_conn_state = STATE_CONNECTING;
}
godot_js_rtc_pc_remote_description_set(_js_id, type.utf8().get_data(), sdp.utf8().get_data(), this, &_on_session_created, &_on_error);
return OK;
}
Error WebRTCPeerConnectionJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
godot_js_rtc_pc_ice_candidate_add(_js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
return OK;
}
Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) {
if (_js_id) {
godot_js_rtc_pc_destroy(_js_id);
_js_id = 0;
}
_conn_state = STATE_NEW;
String config = Variant(p_config).to_json_string();
_js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_gathering_state_changed, &_on_signaling_state_changed, &_on_ice_candidate, &_on_data_channel);
return _js_id ? OK : FAILED;
}
Ref<WebRTCDataChannel> WebRTCPeerConnectionJS::create_data_channel(String p_channel, Dictionary p_channel_config) {
ERR_FAIL_COND_V(_conn_state != STATE_NEW, nullptr);
String config = Variant(p_channel_config).to_json_string();
int id = godot_js_rtc_pc_datachannel_create(_js_id, p_channel.utf8().get_data(), config.utf8().get_data());
ERR_FAIL_COND_V(id == 0, nullptr);
return memnew(WebRTCDataChannelJS(id));
}
Error WebRTCPeerConnectionJS::poll() {
return OK;
}
WebRTCPeerConnection::GatheringState WebRTCPeerConnectionJS::get_gathering_state() const {
return _gathering_state;
}
WebRTCPeerConnection::SignalingState WebRTCPeerConnectionJS::get_signaling_state() const {
return _signaling_state;
}
WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_state() const {
return _conn_state;
}
WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
Dictionary config;
initialize(config);
}
WebRTCPeerConnectionJS::~WebRTCPeerConnectionJS() {
close();
if (_js_id) {
godot_js_rtc_pc_destroy(_js_id);
_js_id = 0;
}
}
#endif

View File

@@ -0,0 +1,90 @@
/**************************************************************************/
/* webrtc_peer_connection_js.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
#ifdef WEB_ENABLED
#include "webrtc_peer_connection.h"
extern "C" {
typedef void (*RTCOnIceConnectionStateChange)(void *p_obj, int p_state);
typedef void (*RTCOnIceGatheringStateChange)(void *p_obj, int p_state);
typedef void (*RTCOnSignalingStateChange)(void *p_obj, int p_state);
typedef void (*RTCOnIceCandidate)(void *p_obj, const char *p_mid, int p_mline_idx, const char *p_candidate);
typedef void (*RTCOnDataChannel)(void *p_obj, int p_id);
typedef void (*RTCOnSession)(void *p_obj, const char *p_type, const char *p_sdp);
typedef void (*RTCOnError)(void *p_obj);
extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_connection_state_change, RTCOnIceGatheringStateChange p_on_gathering_state_change, RTCOnSignalingStateChange p_on_signaling_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
extern void godot_js_rtc_pc_close(int p_id);
extern void godot_js_rtc_pc_destroy(int p_id);
extern void godot_js_rtc_pc_offer_create(int p_id, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
extern void godot_js_rtc_pc_local_description_set(int p_id, const char *p_type, const char *p_sdp, void *p_obj, RTCOnError p_on_error);
extern void godot_js_rtc_pc_remote_description_set(int p_id, const char *p_type, const char *p_sdp, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
extern void godot_js_rtc_pc_ice_candidate_add(int p_id, const char *p_mid_name, int p_mline_idx, const char *p_sdo);
extern int godot_js_rtc_pc_datachannel_create(int p_id, const char *p_label, const char *p_config);
}
class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
GDCLASS(WebRTCPeerConnectionJS, WebRTCPeerConnection);
private:
int _js_id = 0;
ConnectionState _conn_state = STATE_NEW;
GatheringState _gathering_state = GATHERING_STATE_NEW;
SignalingState _signaling_state = SIGNALING_STATE_STABLE;
static void _on_connection_state_changed(void *p_obj, int p_state);
static void _on_gathering_state_changed(void *p_obj, int p_state);
static void _on_signaling_state_changed(void *p_obj, int p_state);
static void _on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate);
static void _on_data_channel(void *p_obj, int p_channel);
static void _on_session_created(void *p_obj, const char *p_type, const char *p_session);
static void _on_error(void *p_obj);
public:
virtual ConnectionState get_connection_state() const override;
virtual GatheringState get_gathering_state() const override;
virtual SignalingState get_signaling_state() const override;
virtual Error initialize(Dictionary configuration = Dictionary()) override;
virtual Ref<WebRTCDataChannel> create_data_channel(String p_channel_name, Dictionary p_channel_config = Dictionary()) override;
virtual Error create_offer() override;
virtual Error set_remote_description(String type, String sdp) override;
virtual Error set_local_description(String type, String sdp) override;
virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) override;
virtual Error poll() override;
virtual void close() override;
WebRTCPeerConnectionJS();
~WebRTCPeerConnectionJS();
};
#endif