webrtc.rst 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. :article_outdated: True
  2. .. _doc_webrtc:
  3. WebRTC
  4. ======
  5. HTML5, WebSocket, WebRTC
  6. ------------------------
  7. One of Godot's great features is its ability to export to the HTML5/WebAssembly platform, allowing your game to run directly in the browser when a user visit your webpage.
  8. This is a great opportunity for both demos and full games, but used to come with some limitations. In the area of networking, browsers used to support only HTTPRequests until recently, when first WebSocket and then WebRTC were proposed as standards.
  9. WebSocket
  10. ^^^^^^^^^
  11. When the WebSocket protocol was standardized in December 2011, it allowed browsers to create stable and bidirectional connections to a WebSocket server. The protocol is a very powerful tool to send push notifications to browsers, and has been used to implement chats, turn-based games, etc.
  12. WebSockets, though, still use a TCP connection, which is good for reliability but not for latency, so not good for real-time applications like VoIP and fast-paced games.
  13. WebRTC
  14. ^^^^^^
  15. For this reason, since 2010, Google started working on a new technology called WebRTC, which later on, in 2017, became a W3C candidate recommendation. WebRTC is a much more complex set of specifications, and relies on many other technologies behind the scenes (ICE, DTLS, SDP) to provide fast, real-time, and secure communication between two peers.
  16. The idea is to find the fastest route between the two peers and establish whenever possible a direct communication (i.e. try to avoid a relaying server).
  17. However, this comes at a price, which is that some media information must be exchanged between the two peers before the communication can start (in the form of Session Description Protocol - SDP strings). This usually takes the form of a so-called WebRTC Signaling Server.
  18. .. image:: img/webrtc_signaling.png
  19. Peers connect to a signaling server (for example a WebSocket server) and send their media information. The server then relays this information to other peers, allowing them to establish the desired direct communication. Once this step is done, peers can disconnect from the signaling server and keep the direct Peer-to-Peer (P2P) connection open.
  20. Using WebRTC in Godot
  21. ---------------------
  22. WebRTC is implemented in Godot via two main classes :ref:`WebRTCPeerConnection <class_WebRTCPeerConnection>` and :ref:`WebRTCDataChannel <class_WebRTCDataChannel>`, plus the multiplayer API implementation :ref:`WebRTCMultiplayerPeer <class_WebRTCMultiplayerPeer>`. See section on :ref:`high-level multiplayer <doc_high_level_multiplayer>` for more details.
  23. .. note:: These classes are available automatically in HTML5, but **require an external GDExtension plugin on native (non-HTML5) platforms**. Check out the `webrtc-native plugin repository <https://github.com/godotengine/webrtc-native>`__ for instructions and to get the latest `release <https://github.com/godotengine/webrtc-native/releases>`__.
  24. .. warning::
  25. When exporting to Android, make sure to enable the ``INTERNET``
  26. permission in the Android export preset before exporting the project or
  27. using one-click deploy. Otherwise, network communication of any kind will be
  28. blocked by Android.
  29. Minimal connection example
  30. ^^^^^^^^^^^^^^^^^^^^^^^^^^
  31. This example will show you how to create a WebRTC connection between two peers in the same application.
  32. This is not very useful in real life, but will give you a good overview of how a WebRTC connection is set up.
  33. ::
  34. extends Node
  35. # Create the two peers
  36. var p1 = WebRTCPeerConnection.new()
  37. var p2 = WebRTCPeerConnection.new()
  38. # And a negotiated channel for each each peer
  39. var ch1 = p1.create_data_channel("chat", {"id": 1, "negotiated": true})
  40. var ch2 = p2.create_data_channel("chat", {"id": 1, "negotiated": true})
  41. func _ready():
  42. # Connect P1 session created to itself to set local description
  43. p1.connect("session_description_created", p1, "set_local_description")
  44. # Connect P1 session and ICE created to p2 set remote description and candidates
  45. p1.connect("session_description_created", p2, "set_remote_description")
  46. p1.connect("ice_candidate_created", p2, "add_ice_candidate")
  47. # Same for P2
  48. p2.connect("session_description_created", p2, "set_local_description")
  49. p2.connect("session_description_created", p1, "set_remote_description")
  50. p2.connect("ice_candidate_created", p1, "add_ice_candidate")
  51. # Let P1 create the offer
  52. p1.create_offer()
  53. # Wait a second and send message from P1
  54. yield(get_tree().create_timer(1), "timeout")
  55. ch1.put_packet("Hi from P1".to_utf8())
  56. # Wait a second and send message from P2
  57. yield(get_tree().create_timer(1), "timeout")
  58. ch2.put_packet("Hi from P2".to_utf8())
  59. func _process(_delta):
  60. # Poll connections
  61. p1.poll()
  62. p2.poll()
  63. # Check for messages
  64. if ch1.get_ready_state() == ch1.STATE_OPEN and ch1.get_available_packet_count() > 0:
  65. print("P1 received: ", ch1.get_packet().get_string_from_utf8())
  66. if ch2.get_ready_state() == ch2.STATE_OPEN and ch2.get_available_packet_count() > 0:
  67. print("P2 received: ", ch2.get_packet().get_string_from_utf8())
  68. This will print:
  69. ::
  70. P1 received: Hi from P1
  71. P2 received: Hi from P2
  72. Local signaling example
  73. ^^^^^^^^^^^^^^^^^^^^^^^
  74. This example expands on the previous one, separating the peers in two different scenes, and using a :ref:`singleton <doc_singletons_autoload>` as a signaling server.
  75. ::
  76. # An example P2P chat client (chat.gd)
  77. extends Node
  78. var peer = WebRTCPeerConnection.new()
  79. # Create negotiated data channel
  80. var channel = peer.create_data_channel("chat", {"negotiated": true, "id": 1})
  81. func _ready():
  82. # Connect all functions
  83. peer.ice_candidate_created.connect(_on_ice_candidate)
  84. peer.session_description_created.connect(_on_session)
  85. # Register to the local signaling server (see below for the implementation)
  86. Signaling.register(get_path())
  87. func _on_ice_candidate(mid, index, sdp):
  88. # Send the ICE candidate to the other peer via signaling server
  89. Signaling.send_candidate(get_path(), mid, index, sdp)
  90. func _on_session(type, sdp):
  91. # Send the session to other peer via signaling server
  92. Signaling.send_session(get_path(), type, sdp)
  93. # Set generated description as local
  94. peer.set_local_description(type, sdp)
  95. func _process(delta):
  96. # Always poll the connection frequently
  97. peer.poll()
  98. if channel.get_ready_state() == WebRTCDataChannel.STATE_OPEN:
  99. while channel.get_available_packet_count() > 0:
  100. print(get_path(), " received: ", channel.get_packet().get_string_from_utf8())
  101. func send_message(message):
  102. channel.put_packet(message.to_utf8())
  103. And now for the local signaling server:
  104. .. note:: This local signaling server is supposed to be used as a :ref:`singleton <doc_singletons_autoload>` to connect two peers in the same scene.
  105. ::
  106. # A local signaling server. Add this to autoloads with name "Signaling" (/root/Signaling)
  107. extends Node
  108. # We will store the two peers here
  109. var peers = []
  110. func register(path):
  111. assert(peers.size() < 2)
  112. peers.append(path)
  113. # If it's the second one, create an offer
  114. if peers.size() == 2:
  115. get_node(peers[0]).peer.create_offer()
  116. func _find_other(path):
  117. # Find the other registered peer.
  118. for p in peers:
  119. if p != path:
  120. return p
  121. return ""
  122. func send_session(path, type, sdp):
  123. var other = _find_other(path)
  124. assert(other != "")
  125. get_node(other).peer.set_remote_description(type, sdp)
  126. func send_candidate(path, mid, index, sdp):
  127. var other = _find_other(path)
  128. assert(other != "")
  129. get_node(other).peer.add_ice_candidate(mid, index, sdp)
  130. Then you can use it like this:
  131. ::
  132. # Main scene (main.gd)
  133. extends Node
  134. const Chat = preload("res://chat.gd")
  135. func _ready():
  136. var p1 = Chat.new()
  137. var p2 = Chat.new()
  138. add_child(p1)
  139. add_child(p2)
  140. yield(get_tree().create_timer(1), "timeout")
  141. p1.send_message("Hi from %s" % p1.get_path())
  142. # Wait a second and send message from P2
  143. yield(get_tree().create_timer(1), "timeout")
  144. p2.send_message("Hi from %s" % p2.get_path())
  145. This will print something similar to this:
  146. ::
  147. /root/main/@@3 received: Hi from /root/main/@@2
  148. /root/main/@@2 received: Hi from /root/main/@@3
  149. Remote signaling with WebSocket
  150. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  151. A more advanced demo using WebSocket for signaling peers and :ref:`WebRTCMultiplayerPeer <class_WebRTCMultiplayerPeer>` is available in the `godot demo projects <https://github.com/godotengine/godot-demo-projects>`_ under `networking/webrtc_signaling`.