diff options
Diffstat (limited to 'modules/webrtc')
-rw-r--r-- | modules/webrtc/SCsub | 6 | ||||
-rw-r--r-- | modules/webrtc/doc_classes/WebRTCPeerConnection.xml | 6 | ||||
-rw-r--r-- | modules/webrtc/library_godot_webrtc.js | 433 | ||||
-rw-r--r-- | modules/webrtc/register_types.cpp | 6 | ||||
-rw-r--r-- | modules/webrtc/register_types.h | 4 | ||||
-rw-r--r-- | modules/webrtc/webrtc_data_channel.cpp | 6 | ||||
-rw-r--r-- | modules/webrtc/webrtc_data_channel.h | 11 | ||||
-rw-r--r-- | modules/webrtc/webrtc_data_channel_gdnative.cpp | 4 | ||||
-rw-r--r-- | modules/webrtc/webrtc_data_channel_gdnative.h | 46 | ||||
-rw-r--r-- | modules/webrtc/webrtc_data_channel_js.cpp | 257 | ||||
-rw-r--r-- | modules/webrtc/webrtc_data_channel_js.h | 68 | ||||
-rw-r--r-- | modules/webrtc/webrtc_multiplayer.cpp | 39 | ||||
-rw-r--r-- | modules/webrtc/webrtc_multiplayer.h | 39 | ||||
-rw-r--r-- | modules/webrtc/webrtc_peer_connection.cpp | 9 | ||||
-rw-r--r-- | modules/webrtc/webrtc_peer_connection.h | 4 | ||||
-rw-r--r-- | modules/webrtc/webrtc_peer_connection_gdnative.cpp | 5 | ||||
-rw-r--r-- | modules/webrtc/webrtc_peer_connection_gdnative.h | 30 | ||||
-rw-r--r-- | modules/webrtc/webrtc_peer_connection_js.cpp | 248 | ||||
-rw-r--r-- | modules/webrtc/webrtc_peer_connection_js.h | 28 |
19 files changed, 685 insertions, 564 deletions
diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub index 20b4c8f8d2..31b8a73bf2 100644 --- a/modules/webrtc/SCsub +++ b/modules/webrtc/SCsub @@ -3,8 +3,6 @@ Import("env") Import("env_modules") -# Thirdparty source files - env_webrtc = env_modules.Clone() use_gdnative = env_webrtc["module_gdnative_enabled"] @@ -12,4 +10,8 @@ if use_gdnative: # GDNative is retained in Javascript for export compatibility env_webrtc.Append(CPPDEFINES=["WEBRTC_GDNATIVE_ENABLED"]) env_webrtc.Prepend(CPPPATH=["#modules/gdnative/include/"]) +if env["platform"] == "javascript": + # Our JavaScript/C++ interface. + env.AddJSLibraries(["library_godot_webrtc.js"]) + env_webrtc.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index 504b4705d8..e21dee8eff 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -40,7 +40,6 @@ <argument index="0" name="label" type="String"> </argument> <argument index="1" name="options" type="Dictionary" default="{ - }"> </argument> <description> @@ -82,7 +81,6 @@ <return type="int" enum="Error"> </return> <argument index="0" name="configuration" type="Dictionary" default="{ - }"> </argument> <description> @@ -97,7 +95,7 @@ { "urls": [ "turn:turn.example.com:3478" ], # One or more TURN servers. "username": "a_username", # Optional username for the TURN server. - "credentials": "a_password", # Optional password for the TURN server. + "credential": "a_password", # Optional password for the TURN server. } ] } @@ -120,7 +118,7 @@ </argument> <description> Sets the SDP description of the local peer. This should be called in response to [signal session_description_created]. - If [code]type[/code] is [code]answer[/code] the peer will start emitting [signal ice_candidate_created]. + 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"> diff --git a/modules/webrtc/library_godot_webrtc.js b/modules/webrtc/library_godot_webrtc.js new file mode 100644 index 0000000000..404a116716 --- /dev/null +++ b/modules/webrtc/library_godot_webrtc.js @@ -0,0 +1,433 @@ +/*************************************************************************/ +/* library_godot_webrtc.js */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +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__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__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__sig: 'ii', + godot_js_rtc_datachannel_is_ordered: function (p_id) { + return IDHandler.get_prop(p_id, 'ordered', true); + }, + + godot_js_rtc_datachannel_id_get__sig: 'ii', + godot_js_rtc_datachannel_id_get: function (p_id) { + return IDHandler.get_prop(p_id, 'id', 65535); + }, + + 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__sig: 'ii', + godot_js_rtc_datachannel_max_retransmits_get: function (p_id) { + return IDHandler.get_prop(p_id, 'maxRetransmits', 65535); + }, + + godot_js_rtc_datachannel_is_negotiated__sig: 'ii', + godot_js_rtc_datachannel_is_negotiated: function (p_id) { + return IDHandler.get_prop(p_id, 'negotiated', 65535); + }, + + 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__sig: 'vi', + godot_js_rtc_datachannel_destroy: function (p_id) { + GodotRTCDataChannel.close(p_id); + IDHandler.remove(p_id); + }, + + 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__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: { + onstatechange: function (p_id, p_conn, callback, event) { + const ref = IDHandler.get(p_id); + if (!ref) { + return; + } + let state; + switch (p_conn.iceConnectionState) { + case 'new': + state = 0; + break; + case 'checking': + state = 1; + break; + case 'connected': + case 'completed': + state = 2; + break; + case 'disconnected': + state = 3; + break; + case 'failed': + state = 4; + break; + case 'closed': + default: + state = 5; + break; + } + callback(state); + }, + + onicecandidate: function (p_id, callback, event) { + const ref = IDHandler.get(p_id); + if (!ref || !event.candidate) { + return; + } + + const c = event.candidate; + const candidate_str = GodotRuntime.allocString(c.candidate); + const mid_str = GodotRuntime.allocString(c.sdpMid); + callback(mid_str, c.sdpMLineIndex, candidate_str); + GodotRuntime.free(candidate_str); + GodotRuntime.free(mid_str); + }, + + ondatachannel: function (p_id, callback, event) { + const ref = IDHandler.get(p_id); + if (!ref) { + return; + } + + const cid = IDHandler.add(event.channel); + callback(cid); + }, + + onsession: function (p_id, callback, session) { + const ref = IDHandler.get(p_id); + if (!ref) { + 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__sig: 'iiiiii', + godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) { + const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref); + const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref); + const ondatachannel = GodotRuntime.get_func(p_on_datachannel).bind(null, p_ref); + + const config = JSON.parse(GodotRuntime.parseString(p_config)); + let conn = null; + try { + conn = new RTCPeerConnection(config); + } catch (e) { + GodotRuntime.error(e); + return 0; + } + + const base = GodotRTCPeerConnection; + const id = IDHandler.add(conn); + conn.oniceconnectionstatechange = base.onstatechange.bind(null, id, conn, onstatechange); + conn.onicecandidate = base.onicecandidate.bind(null, id, oncandidate); + conn.ondatachannel = base.ondatachannel.bind(null, id, ondatachannel); + return id; + }, + + 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__sig: 'vi', + godot_js_rtc_pc_destroy: function (p_id) { + const ref = IDHandler.get(p_id); + if (!ref) { + return; + } + ref.oniceconnectionstatechange = null; + ref.onicecandidate = null; + ref.ondatachannel = null; + IDHandler.remove(p_id); + }, + + 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__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__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__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__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); diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp index 5b296b1ac6..ecfaed9089 100644 --- a/modules/webrtc/register_types.cpp +++ b/modules/webrtc/register_types.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,7 +29,7 @@ /*************************************************************************/ #include "register_types.h" -#include "core/project_settings.h" +#include "core/config/project_settings.h" #include "webrtc_data_channel.h" #include "webrtc_peer_connection.h" diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h index 8f5b9e8452..710ee88a28 100644 --- a/modules/webrtc/register_types.h +++ b/modules/webrtc/register_types.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp index 7566532982..004112f992 100644 --- a/modules/webrtc/webrtc_data_channel.cpp +++ b/modules/webrtc/webrtc_data_channel.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,7 +29,7 @@ /*************************************************************************/ #include "webrtc_data_channel.h" -#include "core/project_settings.h" +#include "core/config/project_settings.h" void WebRTCDataChannel::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &WebRTCDataChannel::poll); diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h index e61f786ca1..20affc513f 100644 --- a/modules/webrtc/webrtc_data_channel.h +++ b/modules/webrtc/webrtc_data_channel.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -73,13 +73,6 @@ public: virtual Error poll() = 0; virtual void close() = 0; - /** Inherited from PacketPeer: **/ - virtual int get_available_packet_count() const = 0; - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0; ///< buffer is GONE after next get_packet - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0; - - virtual int get_max_packet_size() const = 0; - WebRTCDataChannel(); ~WebRTCDataChannel(); }; diff --git a/modules/webrtc/webrtc_data_channel_gdnative.cpp b/modules/webrtc/webrtc_data_channel_gdnative.cpp index 67ad2c07ce..d4cf464c7c 100644 --- a/modules/webrtc/webrtc_data_channel_gdnative.cpp +++ b/modules/webrtc/webrtc_data_channel_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h index be3ea13028..7e02a32046 100644 --- a/modules/webrtc/webrtc_data_channel_gdnative.h +++ b/modules/webrtc/webrtc_data_channel_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WEBRTC_GDNATIVE_ENABLED - #ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H #define WEBRTC_DATA_CHANNEL_GDNATIVE_H +#ifdef WEBRTC_GDNATIVE_ENABLED + #include "modules/gdnative/include/net/godot_net.h" #include "webrtc_data_channel.h" @@ -48,33 +48,33 @@ private: public: void set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl); - virtual void set_write_mode(WriteMode mode); - virtual WriteMode get_write_mode() const; - virtual bool was_string_packet() const; + virtual 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; - virtual String get_label() const; - virtual bool is_ordered() const; - virtual int get_id() const; - virtual int get_max_packet_life_time() const; - virtual int get_max_retransmits() const; - virtual String get_protocol() const; - virtual bool is_negotiated() const; + virtual 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 Error poll(); - virtual void close(); + virtual Error poll() override; + virtual void close() override; /** Inherited from PacketPeer: **/ - virtual int get_available_packet_count() const; - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + 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; + virtual int get_max_packet_size() const override; WebRTCDataChannelGDNative(); ~WebRTCDataChannelGDNative(); }; -#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H - #endif // WEBRTC_GDNATIVE_ENABLED + +#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp index 1b360720a2..dfbec80c86 100644 --- a/modules/webrtc/webrtc_data_channel_js.cpp +++ b/modules/webrtc/webrtc_data_channel_js.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,40 +34,43 @@ #include "emscripten.h" extern "C" { -EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_error(void *obj) { - WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj); - peer->_on_error(); -} - -EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_open(void *obj) { - WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj); - peer->_on_open(); -} - -EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_close(void *obj) { - WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj); - peer->_on_close(); -} +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); -EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) { - WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj); - peer->_on_message(p_data, p_size, p_is_string); -} +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 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() { - in_buffer.resize(_in_buffer_shift); +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() { - close(); +void WebRTCDataChannelJS::_on_close(void *p_obj) { + WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj); + peer->close(); } -void WebRTCDataChannelJS::_on_error() { - close(); +void WebRTCDataChannelJS::_on_error(void *p_obj) { + WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj); + peer->close(); } -void WebRTCDataChannelJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) { +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."); @@ -75,25 +78,14 @@ void WebRTCDataChannelJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_i in_buffer.write((uint8_t *)&p_size, 4); in_buffer.write((uint8_t *)&is_string, 1); in_buffer.write(p_data, p_size); - queue_count++; + peer->queue_count++; } void WebRTCDataChannelJS::close() { in_buffer.resize(0); queue_count = 0; _was_string = false; - /* clang-format off */ - EM_ASM({ - var dict = Module.IDHandler.get($0); - if (!dict) return; - var channel = dict["channel"]; - channel.onopen = null; - channel.onclose = null; - channel.onerror = null; - channel.onmessage = null; - channel.close(); - }, _js_id); - /* clang-format on */ + godot_js_rtc_datachannel_close(_js_id); } Error WebRTCDataChannelJS::poll() { @@ -101,24 +93,7 @@ Error WebRTCDataChannelJS::poll() { } WebRTCDataChannelJS::ChannelState WebRTCDataChannelJS::get_ready_state() const { - /* clang-format off */ - return (ChannelState) EM_ASM_INT({ - var dict = Module.IDHandler.get($0); - if (!dict) return 3; // CLOSED - var channel = dict["channel"]; - switch(channel.readyState) { - case "connecting": - return 0; - case "open": - return 1; - case "closing": - return 2; - case "closed": - return 3; - } - return 3; // CLOSED - }, _js_id); - /* clang-format on */ + return (ChannelState)godot_js_rtc_datachannel_ready_state_get(_js_id); } int WebRTCDataChannelJS::get_available_packet_count() const { @@ -158,27 +133,7 @@ Error WebRTCDataChannelJS::put_packet(const uint8_t *p_buffer, int p_buffer_size ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED); int is_bin = _write_mode == WebRTCDataChannel::WRITE_MODE_BINARY ? 1 : 0; - - /* clang-format off */ - EM_ASM({ - var dict = Module.IDHandler.get($0); - var channel = dict["channel"]; - var bytes_array = new Uint8Array($2); - var i = 0; - - for(i=0; i<$2; i++) { - bytes_array[i] = getValue($1+i, 'i8'); - } - - if ($3) { - channel.send(bytes_array.buffer); - } else { - var string = new TextDecoder("utf-8").decode(bytes_array); - channel.send(string); - } - }, _js_id, p_buffer, p_buffer_size, is_bin); - /* clang-format on */ - + godot_js_rtc_datachannel_send(_js_id, p_buffer, p_buffer_size, is_bin); return OK; } @@ -202,46 +157,20 @@ String WebRTCDataChannelJS::get_label() const { return _label; } -/* clang-format off */ -#define _JS_GET(PROP, DEF) \ -EM_ASM_INT({ \ - var dict = Module.IDHandler.get($0); \ - if (!dict || !dict["channel"]) { \ - return DEF; \ - } \ - var out = dict["channel"].PROP; \ - return out === null ? DEF : out; \ -}, _js_id) -/* clang-format on */ - bool WebRTCDataChannelJS::is_ordered() const { - return _JS_GET(ordered, true); + return godot_js_rtc_datachannel_is_ordered(_js_id); } int WebRTCDataChannelJS::get_id() const { - return _JS_GET(id, 65535); + return godot_js_rtc_datachannel_id_get(_js_id); } int WebRTCDataChannelJS::get_max_packet_life_time() const { - // Can't use macro, webkit workaround. - /* clang-format off */ - return EM_ASM_INT({ - var dict = Module.IDHandler.get($0); - if (!dict || !dict["channel"]) { - return 65535; - } - if (dict["channel"].maxRetransmitTime !== undefined) { - // Guess someone didn't appreciate the standardization process. - return dict["channel"].maxRetransmitTime; - } - var out = dict["channel"].maxPacketLifeTime; - return out === null ? 65535 : out; - }, _js_id); - /* clang-format on */ + return godot_js_rtc_datachannel_max_packet_lifetime_get(_js_id); } int WebRTCDataChannelJS::get_max_retransmits() const { - return _JS_GET(maxRetransmits, 65535); + return godot_js_rtc_datachannel_max_retransmits_get(_js_id); } String WebRTCDataChannelJS::get_protocol() const { @@ -249,117 +178,31 @@ String WebRTCDataChannelJS::get_protocol() const { } bool WebRTCDataChannelJS::is_negotiated() const { - return _JS_GET(negotiated, false); + return godot_js_rtc_datachannel_is_negotiated(_js_id); } WebRTCDataChannelJS::WebRTCDataChannelJS() { - queue_count = 0; - _was_string = false; - _write_mode = WRITE_MODE_BINARY; - _js_id = 0; } WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) { - queue_count = 0; - _was_string = false; - _write_mode = WRITE_MODE_BINARY; _js_id = js_id; - /* clang-format off */ - EM_ASM({ - var c_ptr = $0; - var dict = Module.IDHandler.get($1); - if (!dict) return; - var channel = dict["channel"]; - dict["ptr"] = c_ptr; - - channel.binaryType = "arraybuffer"; - channel.onopen = function (evt) { - ccall("_emrtc_on_ch_open", - "void", - ["number"], - [c_ptr] - ); - }; - channel.onclose = function (evt) { - ccall("_emrtc_on_ch_close", - "void", - ["number"], - [c_ptr] - ); - }; - channel.onerror = function (evt) { - ccall("_emrtc_on_ch_error", - "void", - ["number"], - [c_ptr] - ); - }; - channel.onmessage = function(event) { - var buffer; - var is_string = 0; - if (event.data instanceof ArrayBuffer) { - buffer = new Uint8Array(event.data); - } else if (event.data instanceof Blob) { - console.error("Blob type not supported"); - return; - } else if (typeof event.data === "string") { - is_string = 1; - var enc = new TextEncoder("utf-8"); - buffer = new Uint8Array(enc.encode(event.data)); - } else { - console.error("Unknown message type"); - return; - } - var len = buffer.length*buffer.BYTES_PER_ELEMENT; - var out = Module._malloc(len); - Module.HEAPU8.set(buffer, out); - ccall("_emrtc_on_ch_message", - "void", - ["number", "number", "number", "number"], - [c_ptr, out, len, is_string] - ); - Module._free(out); - } - - }, this, js_id); + godot_js_rtc_datachannel_connect(js_id, this, &_on_open, &_on_message, &_on_error, &_on_close); // Parse label - char *str; - str = (char *)EM_ASM_INT({ - var dict = Module.IDHandler.get($0); - if (!dict || !dict["channel"]) return 0; - var str = dict["channel"].label; - var len = lengthBytesUTF8(str)+1; - var ptr = _malloc(str); - stringToUTF8(str, ptr, len+1); - return ptr; - }, js_id); - if(str != nullptr) { - _label.parse_utf8(str); - EM_ASM({ _free($0) }, str); + char *label = godot_js_rtc_datachannel_label_get(js_id); + if (label) { + _label.parse_utf8(label); + free(label); } - str = (char *)EM_ASM_INT({ - var dict = Module.IDHandler.get($0); - if (!dict || !dict["channel"]) return 0; - var str = dict["channel"].protocol; - var len = lengthBytesUTF8(str)+1; - var ptr = _malloc(str); - stringToUTF8(str, ptr, len+1); - return ptr; - }, js_id); - if(str != nullptr) { - _protocol.parse_utf8(str); - EM_ASM({ _free($0) }, str); + char *protocol = godot_js_rtc_datachannel_protocol_get(js_id); + if (protocol) { + _protocol.parse_utf8(protocol); + free(protocol); } - /* clang-format on */ } WebRTCDataChannelJS::~WebRTCDataChannelJS() { close(); - /* clang-format off */ - EM_ASM({ - Module.IDHandler.remove($0); - }, _js_id); - /* clang-format on */ -}; + godot_js_rtc_datachannel_destroy(_js_id); +} #endif diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h index 00b5963ea1..db58ebccff 100644 --- a/modules/webrtc/webrtc_data_channel_js.h +++ b/modules/webrtc/webrtc_data_channel_js.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef JAVASCRIPT_ENABLED - #ifndef WEBRTC_DATA_CHANNEL_JS_H #define WEBRTC_DATA_CHANNEL_JS_H +#ifdef JAVASCRIPT_ENABLED + #include "webrtc_data_channel.h" class WebRTCDataChannelJS : public WebRTCDataChannel { @@ -42,52 +42,52 @@ private: String _label; String _protocol; - bool _was_string; - WriteMode _write_mode; + 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; + int _js_id = 0; RingBuffer<uint8_t> in_buffer; - int queue_count; + 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: - void _on_open(); - void _on_close(); - void _on_error(); - void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string); - - virtual void set_write_mode(WriteMode mode); - virtual WriteMode get_write_mode() const; - virtual bool was_string_packet() const; - - virtual ChannelState get_ready_state() const; - virtual String get_label() const; - virtual bool is_ordered() const; - virtual int get_id() const; - virtual int get_max_packet_life_time() const; - virtual int get_max_retransmits() const; - virtual String get_protocol() const; - virtual bool is_negotiated() const; - - virtual Error poll(); - virtual void close(); + 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 Error poll() override; + virtual void close() override; /** Inherited from PacketPeer: **/ - virtual int get_available_packet_count() const; - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + 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; + virtual int get_max_packet_size() const override; WebRTCDataChannelJS(); WebRTCDataChannelJS(int js_id); ~WebRTCDataChannelJS(); }; -#endif // WEBRTC_DATA_CHANNEL_JS_H - #endif // JAVASCRIPT_ENABLED + +#endif // WEBRTC_DATA_CHANNEL_JS_H diff --git a/modules/webrtc/webrtc_multiplayer.cpp b/modules/webrtc/webrtc_multiplayer.cpp index 78a4d1e61a..741cad5640 100644 --- a/modules/webrtc/webrtc_multiplayer.cpp +++ b/modules/webrtc/webrtc_multiplayer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -65,8 +65,9 @@ bool WebRTCMultiplayer::is_server() const { } void WebRTCMultiplayer::poll() { - if (peer_map.size() == 0) + if (peer_map.size() == 0) { return; + } List<int> remove; List<int> add; @@ -113,15 +114,17 @@ void WebRTCMultiplayer::poll() { // Remove disconnected peers for (List<int>::Element *E = remove.front(); E; E = E->next()) { remove_peer(E->get()); - if (next_packet_peer == E->get()) + if (next_packet_peer == E->get()) { next_packet_peer = 0; + } } // Signal newly connected peers for (List<int>::Element *E = add.front(); E; E = E->next()) { // Already connected to server: simply notify new peer. // NOTE: Mesh is always connected. - if (connection_status == CONNECTION_CONNECTED) + if (connection_status == CONNECTION_CONNECTED) { emit_signal("peer_connected", E->get()); + } // Server emulation mode suppresses peer_conencted until server connects. if (server_compat && E->get() == TARGET_PEER_SERVER) { @@ -131,20 +134,24 @@ void WebRTCMultiplayer::poll() { emit_signal("connection_succeeded"); // Notify of all previously connected peers for (Map<int, Ref<ConnectedPeer>>::Element *F = peer_map.front(); F; F = F->next()) { - if (F->key() != 1 && F->get()->connected) + if (F->key() != 1 && F->get()->connected) { emit_signal("peer_connected", F->key()); + } } break; // Because we already notified of all newly added peers. } } // Fetch next packet - if (next_packet_peer == 0) + if (next_packet_peer == 0) { _find_next_peer(); + } } void WebRTCMultiplayer::_find_next_peer() { Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.find(next_packet_peer); - if (E) E = E->next(); + if (E) { + E = E->next(); + } // After last. while (E) { for (List<Ref<WebRTCDataChannel>>::Element *F = E->get()->channels.front(); F; F = F->next()) { @@ -164,8 +171,9 @@ void WebRTCMultiplayer::_find_next_peer() { return; } } - if (E->key() == (int)next_packet_peer) + if (E->key() == (int)next_packet_peer) { break; + } E = E->next(); } // No packet found @@ -190,10 +198,11 @@ Error WebRTCMultiplayer::initialize(int p_self_id, bool p_server_compat) { server_compat = p_server_compat; // Mesh and server are always connected - if (!server_compat || p_self_id == 1) + if (!server_compat || p_self_id == 1) { connection_status = CONNECTION_CONNECTED; - else + } else { connection_status = CONNECTION_CONNECTING; + } return OK; } @@ -319,7 +328,6 @@ Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size) Map<int, Ref<ConnectedPeer>>::Element *E = nullptr; if (target_peer > 0) { - E = peer_map.find(target_peer); ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + "."); @@ -331,10 +339,10 @@ Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size) int exclude = -target_peer; for (Map<int, Ref<ConnectedPeer>>::Element *F = peer_map.front(); F; F = F->next()) { - // Exclude packet. If target_peer == 0 then don't exclude any packets - if (target_peer != 0 && F->key() == exclude) + if (target_peer != 0 && F->key() == exclude) { continue; + } ERR_CONTINUE(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid()); F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size); @@ -344,8 +352,9 @@ Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size) } int WebRTCMultiplayer::get_available_packet_count() const { - if (next_packet_peer == 0) + if (next_packet_peer == 0) { return 0; // To be sure next call to get_packet works if size > 0 . + } int size = 0; for (Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.front(); E; E = E->next()) { for (List<Ref<WebRTCDataChannel>>::Element *F = E->get()->channels.front(); F; F = F->next()) { diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer.h index 0e1335b8a8..6b4ae6fcc8 100644 --- a/modules/webrtc/webrtc_multiplayer.h +++ b/modules/webrtc/webrtc_multiplayer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,6 @@ #include "webrtc_peer_connection.h" class WebRTCMultiplayer : public NetworkedMultiplayerPeer { - GDCLASS(WebRTCMultiplayer, NetworkedMultiplayerPeer); protected: @@ -50,7 +49,6 @@ private: }; class ConnectedPeer : public Reference { - public: Ref<WebRTCPeerConnection> connection; List<Ref<WebRTCDataChannel>> channels; @@ -58,8 +56,9 @@ private: ConnectedPeer() { connected = false; - for (int i = 0; i < CH_RESERVED_MAX; i++) + for (int i = 0; i < CH_RESERVED_MAX; i++) { channels.push_front(Ref<WebRTCDataChannel>()); + } } }; @@ -90,27 +89,27 @@ public: void close(); // PacketPeer - Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet - Error put_packet(const uint8_t *p_buffer, int p_buffer_size); - int get_available_packet_count() const; - int get_max_packet_size() const; + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + int get_available_packet_count() const override; + int get_max_packet_size() const override; // NetworkedMultiplayerPeer - void set_transfer_mode(TransferMode p_mode); - TransferMode get_transfer_mode() const; - void set_target_peer(int p_peer_id); + void set_transfer_mode(TransferMode p_mode) override; + TransferMode get_transfer_mode() const override; + void set_target_peer(int p_peer_id) override; - int get_unique_id() const; - int get_packet_peer() const; + int get_unique_id() const override; + int get_packet_peer() const override; - bool is_server() const; + bool is_server() const override; - void poll(); + void poll() override; - void set_refuse_new_connections(bool p_enable); - bool is_refusing_new_connections() const; + void set_refuse_new_connections(bool p_enable) override; + bool is_refusing_new_connections() const override; - ConnectionStatus get_connection_status() const; + ConnectionStatus get_connection_status() const override; }; -#endif +#endif // WEBRTC_MULTIPLAYER_H diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp index 399e4f09ff..3e2938bf7d 100644 --- a/modules/webrtc/webrtc_peer_connection.cpp +++ b/modules/webrtc/webrtc_peer_connection.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,14 +33,13 @@ WebRTCPeerConnection *(*WebRTCPeerConnection::_create)() = nullptr; Ref<WebRTCPeerConnection> WebRTCPeerConnection::create_ref() { - return create(); } WebRTCPeerConnection *WebRTCPeerConnection::create() { - - if (!_create) + if (!_create) { return nullptr; + } return _create(); } diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h index 7366c3d0e8..ae75864489 100644 --- a/modules/webrtc/webrtc_peer_connection.h +++ b/modules/webrtc/webrtc_peer_connection.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.cpp b/modules/webrtc/webrtc_peer_connection_gdnative.cpp index f082646629..dcf78dfb73 100644 --- a/modules/webrtc/webrtc_peer_connection_gdnative.cpp +++ b/modules/webrtc/webrtc_peer_connection_gdnative.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -49,7 +49,6 @@ Error WebRTCPeerConnectionGDNative::set_default_library(const godot_net_webrtc_l } WebRTCPeerConnection *WebRTCPeerConnectionGDNative::_create() { - WebRTCPeerConnectionGDNative *obj = memnew(WebRTCPeerConnectionGDNative); ERR_FAIL_COND_V_MSG(!default_library, obj, "Default GDNative WebRTC implementation not defined."); diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_gdnative.h index 8e59ad62ba..578af0202f 100644 --- a/modules/webrtc/webrtc_peer_connection_gdnative.h +++ b/modules/webrtc/webrtc_peer_connection_gdnative.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef WEBRTC_GDNATIVE_ENABLED - #ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H #define WEBRTC_PEER_CONNECTION_GDNATIVE_H +#ifdef WEBRTC_GDNATIVE_ENABLED + #include "modules/gdnative/include/net/godot_net.h" #include "webrtc_peer_connection.h" @@ -53,21 +53,21 @@ public: void set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl); - virtual ConnectionState get_connection_state() const; + virtual ConnectionState get_connection_state() const override; - virtual Error initialize(Dictionary p_config = Dictionary()); - virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = Dictionary()); - virtual Error create_offer(); - virtual Error set_remote_description(String type, String sdp); - virtual Error set_local_description(String type, String sdp); - virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName); - virtual Error poll(); - virtual void close(); + virtual Error initialize(Dictionary p_config = Dictionary()) override; + virtual Ref<WebRTCDataChannel> create_data_channel(String p_label, Dictionary p_options = 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; WebRTCPeerConnectionGDNative(); ~WebRTCPeerConnectionGDNative(); }; -#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H - #endif // WEBRTC_GDNATIVE_ENABLED + +#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp index 593c3a5162..8879f7d6ec 100644 --- a/modules/webrtc/webrtc_peer_connection_js.cpp +++ b/modules/webrtc/webrtc_peer_connection_js.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,116 +37,32 @@ #include "core/io/json.h" #include "emscripten.h" -extern "C" { -EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) { - WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj); - peer->emit_signal("ice_candidate_created", String(p_MidName), p_MlineIndexName, String(p_sdpName)); +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("ice_candidate_created", String(p_mid_name), p_mline_idx, String(p_candidate)); } -EMSCRIPTEN_KEEPALIVE void _emrtc_session_description_created(void *obj, char *p_type, char *p_offer) { - WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj); - peer->emit_signal("session_description_created", String(p_type), String(p_offer)); +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("session_description_created", String(p_type), String(p_session)); } -EMSCRIPTEN_KEEPALIVE void _emrtc_on_connection_state_changed(void *obj) { - WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj); - peer->_on_connection_state_changed(); +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; } -EMSCRIPTEN_KEEPALIVE void _emrtc_on_error() { +void WebRTCPeerConnectionJS::_on_error(void *p_obj) { ERR_PRINT("RTCPeerConnection error!"); } -EMSCRIPTEN_KEEPALIVE void _emrtc_emit_channel(void *obj, int p_id) { - WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj); +void WebRTCPeerConnectionJS::_on_data_channel(void *p_obj, int p_id) { + WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj); peer->emit_signal("data_channel_received", Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id))); } -} - -void _emrtc_create_pc(int p_id, const Dictionary &p_config) { - String config = JSON::print(p_config); - /* clang-format off */ - EM_ASM({ - var dict = Module.IDHandler.get($0); - var c_ptr = dict["ptr"]; - var config = JSON.parse(UTF8ToString($1)); - // Setup local connaction - var conn = null; - try { - conn = new RTCPeerConnection(config); - } catch (e) { - console.log(e); - return; - } - conn.oniceconnectionstatechange = function(event) { - if (!Module.IDHandler.get($0)) return; - ccall("_emrtc_on_connection_state_changed", "void", ["number"], [c_ptr]); - }; - conn.onicecandidate = function(event) { - if (!Module.IDHandler.get($0)) return; - if (!event.candidate) return; - - var c = event.candidate; - // should emit on ice candidate - ccall("_emrtc_on_ice_candidate", - "void", - ["number", "string", "number", "string"], - [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate] - ); - }; - conn.ondatachannel = function (evt) { - var dict = Module.IDHandler.get($0); - if (!dict) { - return; - } - var id = Module.IDHandler.add({"channel": evt.channel, "ptr": null}); - ccall("_emrtc_emit_channel", - "void", - ["number", "number"], - [c_ptr, id] - ); - }; - dict["conn"] = conn; - }, p_id, config.utf8().get_data()); - /* clang-format on */ -} - -void WebRTCPeerConnectionJS::_on_connection_state_changed() { - /* clang-format off */ - _conn_state = (ConnectionState)EM_ASM_INT({ - var dict = Module.IDHandler.get($0); - if (!dict) return 5; // CLOSED - var conn = dict["conn"]; - switch(conn.iceConnectionState) { - case "new": - return 0; - case "checking": - return 1; - case "connected": - case "completed": - return 2; - case "disconnected": - return 3; - case "failed": - return 4; - case "closed": - return 5; - } - return 5; // CLOSED - }, _js_id); - /* clang-format on */ -} void WebRTCPeerConnectionJS::close() { - /* clang-format off */ - EM_ASM({ - var dict = Module.IDHandler.get($0); - if (!dict) return; - if (dict["conn"]) { - dict["conn"].close(); - } - }, _js_id); - /* clang-format on */ + godot_js_rtc_pc_close(_js_id); _conn_state = STATE_CLOSED; } @@ -154,46 +70,12 @@ Error WebRTCPeerConnectionJS::create_offer() { ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED); _conn_state = STATE_CONNECTING; - /* clang-format off */ - EM_ASM({ - var dict = Module.IDHandler.get($0); - var conn = dict["conn"]; - var c_ptr = dict["ptr"]; - var onError = function(error) { - console.error(error); - ccall("_emrtc_on_error", "void", [], []); - }; - var onCreated = function(offer) { - ccall("_emrtc_session_description_created", - "void", - ["number", "string", "string"], - [c_ptr, offer.type, offer.sdp] - ); - }; - conn.createOffer().then(onCreated).catch(onError); - }, _js_id); - /* clang-format on */ + 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) { - /* clang-format off */ - EM_ASM({ - var dict = Module.IDHandler.get($0); - var conn = dict["conn"]; - var c_ptr = dict["ptr"]; - var type = UTF8ToString($1); - var sdp = UTF8ToString($2); - var onError = function(error) { - console.error(error); - ccall("_emrtc_on_error", "void", [], []); - }; - conn.setLocalDescription({ - "sdp": sdp, - "type": type - }).catch(onError); - }, _js_id, type.utf8().get_data(), sdp.utf8().get_data()); - /* clang-format on */ + godot_js_rtc_pc_local_description_set(_js_id, type.utf8().get_data(), sdp.utf8().get_data(), this, &_on_error); return OK; } @@ -202,83 +84,32 @@ Error WebRTCPeerConnectionJS::set_remote_description(String type, String sdp) { ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED); _conn_state = STATE_CONNECTING; } - /* clang-format off */ - EM_ASM({ - var dict = Module.IDHandler.get($0); - var conn = dict["conn"]; - var c_ptr = dict["ptr"]; - var type = UTF8ToString($1); - var sdp = UTF8ToString($2); - - var onError = function(error) { - console.error(error); - ccall("_emrtc_on_error", "void", [], []); - }; - var onCreated = function(offer) { - ccall("_emrtc_session_description_created", - "void", - ["number", "string", "string"], - [c_ptr, offer.type, offer.sdp] - ); - }; - var onSet = function() { - if (type != "offer") { - return; - } - conn.createAnswer().then(onCreated); - }; - conn.setRemoteDescription({ - "sdp": sdp, - "type": type - }).then(onSet).catch(onError); - }, _js_id, type.utf8().get_data(), sdp.utf8().get_data()); - /* clang-format on */ + 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) { - /* clang-format off */ - EM_ASM({ - var dict = Module.IDHandler.get($0); - var conn = dict["conn"]; - var c_ptr = dict["ptr"]; - var sdpMidName = UTF8ToString($1); - var sdpMlineIndexName = UTF8ToString($2); - var sdpName = UTF8ToString($3); - conn.addIceCandidate(new RTCIceCandidate({ - "candidate": sdpName, - "sdpMid": sdpMidName, - "sdpMlineIndex": sdpMlineIndexName - })); - }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data()); - /* clang-format on */ + 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) { - _emrtc_create_pc(_js_id, p_config); - return OK; + if (_js_id) { + godot_js_rtc_pc_destroy(_js_id); + _js_id = 0; + } + _conn_state = STATE_NEW; + + String config = JSON::print(p_config); + _js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_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 = JSON::print(p_channel_config); - /* clang-format off */ - int id = EM_ASM_INT({ - try { - var dict = Module.IDHandler.get($0); - if (!dict) return 0; - var label = UTF8ToString($1); - var config = JSON.parse(UTF8ToString($2)); - var conn = dict["conn"]; - return Module.IDHandler.add({ - "channel": conn.createDataChannel(label, config), - "ptr": null - }) - } catch (e) { - return 0; - } - }, _js_id, p_channel.utf8().get_data(), config.utf8().get_data()); - /* clang-format on */ + 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)); } @@ -293,22 +124,17 @@ WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_sta WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() { _conn_state = STATE_NEW; + _js_id = 0; - /* clang-format off */ - _js_id = EM_ASM_INT({ - return Module.IDHandler.add({"conn": null, "ptr": $0}); - }, this); - /* clang-format on */ Dictionary config; - _emrtc_create_pc(_js_id, config); + initialize(config); } WebRTCPeerConnectionJS::~WebRTCPeerConnectionJS() { close(); - /* clang-format off */ - EM_ASM({ - Module.IDHandler.remove($0); - }, _js_id); - /* clang-format on */ + if (_js_id) { + godot_js_rtc_pc_destroy(_js_id); + _js_id = 0; + } }; #endif diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h index 6540077e84..0272e67f6f 100644 --- a/modules/webrtc/webrtc_peer_connection_js.h +++ b/modules/webrtc/webrtc_peer_connection_js.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,17 +35,37 @@ #include "webrtc_peer_connection.h" -class WebRTCPeerConnectionJS : public WebRTCPeerConnection { +extern "C" { +typedef void (*RTCOnIceConnectionStateChange)(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_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 { private: int _js_id; ConnectionState _conn_state; + static void _on_connection_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: static WebRTCPeerConnection *_create() { return memnew(WebRTCPeerConnectionJS); } static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionJS::_create; } - void _on_connection_state_changed(); virtual ConnectionState get_connection_state() const; virtual Error initialize(Dictionary configuration = Dictionary()); |