Bläddra i källkod

Update WebSocket documentation for Godot 4

Co-authored-by: A Thousand Ships <[email protected]>
Co-authored-by: Hugo Locurcio <[email protected]>
Co-authored-by: Fabio Alessandrelli <[email protected]>
TranquilMarmot 2 år sedan
förälder
incheckning
3f0f9ed6df
1 ändrade filer med 97 tillägg och 72 borttagningar
  1. 97 72
      tutorials/networking/websocket.rst

+ 97 - 72
tutorials/networking/websocket.rst

@@ -1,21 +1,19 @@
-:article_outdated: True
-
 .. _doc_websocket:
 
-WebSocket
-=========
+Using WebSockets
+================
 
 HTML5 and WebSocket
 -------------------
 
 The WebSocket protocol was standardized in 2011 with the original goal of allowing browsers to create stable and bidirectional connections with a server.
-Before that, browsers used to only support HTTPRequests, which is not well-suited for bidirectional communication.
+Before that, browsers used to only support HTTP requests, which aren't well-suited for bidirectional communication.
 
-The protocol is message based and a very powerful tool to send push notifications to browsers, and has been used to implement chats, turn-based games, etc. It still uses 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 (see :ref:`WebRTC <doc_webrtc>` for those use cases).
+The protocol is message-based and a very powerful tool to send push notifications to browsers. It has been used to implement chats, turn-based games, and more. It still uses a TCP connection, which is good for reliability but not for latency, so it's not good for real-time applications like VoIP and fast-paced games (see :ref:`WebRTC <doc_webrtc>` for those use cases).
 
-Due to its simplicity, its wide compatibility, and being easier to use than a raw TCP connection, WebSocket soon started to spread outside the browsers, in native applications as a mean to communicate with network servers.
+Due to its simplicity, its wide compatibility, and being easier to use than a raw TCP connection, WebSocket started to spread outside the browsers, in native applications as a mean to communicate with network servers.
 
-Godot supports WebSocket in both native and HTML5 exports.
+Godot supports WebSocket in both native and web exports.
 
 Using WebSocket in Godot
 ------------------------
@@ -42,124 +40,151 @@ This example will show you how to create a WebSocket connection to a remote serv
     extends Node
 
     # The URL we will connect to.
+    # Use "ws://localhost:9080" if testing with the minimal server example below.
+    # `wss://` is used for secure connections,
+    # while `ws://` is used for plain text (insecure) connections.
     @export var websocket_url = "wss://echo.websocket.org"
 
     # Our WebSocketClient instance.
     var socket = WebSocketPeer.new()
 
+
     func _ready():
         # Initiate connection to the given URL.
         var err = socket.connect_to_url(websocket_url)
-        if err != OK:
-            print("Unable to connect")
-            set_process(false)
-        else:
+        if err == OK:
+            print("Connecting to %s..." % websocket_url)
             # Wait for the socket to connect.
             await get_tree().create_timer(2).timeout
-            
+
             # Send data.
+            print("> Sending test packet.")
             socket.send_text("Test packet")
+        else:
+            push_error("Unable to connect.")
+            set_process(false)
+
 
     func _process(_delta):
-        # Call this in _process or _physics_process. Data transfer and state updates
-        # will only happen when calling this function.
+        # Call this in `_process()` or `_physics_process()`.
+        # Data transfer and state updates will only happen when calling this function.
         socket.poll()
-        
+
         # get_ready_state() tells you what state the socket is in.
         var state = socket.get_ready_state()
-        
-        # WebSocketPeer.STATE_OPEN means the socket is connected and ready
+
+        # `WebSocketPeer.STATE_OPEN` means the socket is connected and ready
         # to send and receive data.
         if state == WebSocketPeer.STATE_OPEN:
             while socket.get_available_packet_count():
-                print("Got data from server: ", socket.get_packet().get_string_from_utf8())
-        
-        # WebSocketPeer.STATE_CLOSING means the socket is closing.
+                var packet = socket.get_packet()
+                if socket.was_string_packet():
+                    var packet_text = packet.get_string_from_utf8()
+                    print("< Got text data from server: %s" % packet_text)
+                else:
+                    print("< Got binary data from server: %d bytes" % packet.size())
+
+        # `WebSocketPeer.STATE_CLOSING` means the socket is closing.
         # It is important to keep polling for a clean close.
         elif state == WebSocketPeer.STATE_CLOSING:
             pass
-        
-        # WebSocketPeer.STATE_CLOSED means the connection has fully closed.
+
+        # `WebSocketPeer.STATE_CLOSED` means the connection has fully closed.
         # It is now safe to stop polling.
         elif state == WebSocketPeer.STATE_CLOSED:
-            # The code will be -1 if the disconnection was not properly notified by the remote peer.
+            # The code will be `-1` if the disconnection was not properly notified by the remote peer.
             var code = socket.get_close_code()
             print("WebSocket closed with code: %d. Clean: %s" % [code, code != -1])
             set_process(false) # Stop processing.
 
-
 This will print something similar to:
 
+.. highlight:: none
+
 ::
 
-    Got data from server: Request served by 7811941c69e658
-    Got data from server: Test packet
+    Connecting to wss://echo.websocket.org...
+    < Got text data from server: Request served by 7811941c69e658
+    > Sending test packet.
+    < Got text data from server: Test packet
 
 Minimal server example
 ~~~~~~~~~~~~~~~~~~~~~~
 
 This example will show you how to create a WebSocket server that listens for remote connections, and how to send and receive data.
 
+.. highlight:: gdscript
+
 ::
 
     extends Node
 
-    # The port we will listen to
+    # The port we will listen to.
     const PORT = 9080
-    # Our WebSocketServer instance
-    var _server = WebSocketServer.new()
 
-    func _ready():
-        # Connect base signals to get notified of new client connections,
-        # disconnections, and disconnect requests.
-        _server.client_connected.connect(_connected)
-        _server.client_disconnected.connect(_disconnected)
-        _server.client_close_request.connect(_close_request)
-        # This signal is emitted when not using the Multiplayer API every time a
-        # full packet is received.
-        # Alternatively, you could check get_peer(PEER_ID).get_available_packets()
-        # in a loop for each connected peer.
-        _server.data_received.connect(_on_data)
-        # Start listening on the given port.
-        var err = _server.listen(PORT)
-        if err != OK:
-            print("Unable to start server")
-            set_process(false)
+    # Our TCP Server instance.
+    var _tcp_server = TCPServer.new()
 
-    func _connected(id, proto):
-        # This is called when a new peer connects, "id" will be the assigned peer id,
-        # "proto" will be the selected WebSocket sub-protocol (which is optional)
-        print("Client %d connected with protocol: %s" % [id, proto])
+    # Our connected peers list.
+    var _peers: Dictionary[int, WebSocketPeer] = {}
 
-    func _close_request(id, code, reason):
-        # This is called when a client notifies that it wishes to close the connection,
-        # providing a reason string and close code.
-        print("Client %d disconnecting with code: %d, reason: %s" % [id, code, reason])
+    var last_peer_id := 1
 
-    func _disconnected(id, was_clean = false):
-        # This is called when a client disconnects, "id" will be the one of the
-        # disconnecting client, "was_clean" will tell you if the disconnection
-        # was correctly notified by the remote peer before closing the socket.
-        print("Client %d disconnected, clean: %s" % [id, str(was_clean)])
 
-    func _on_data(id):
-        # Print the received packet, you MUST always use get_peer(id).get_packet to receive data,
-        # and not get_packet directly when not using the MultiplayerAPI.
-        var pkt = _server.get_peer(id).get_packet()
-        print("Got data from client %d: %s ... echoing" % [id, pkt.get_string_from_utf8()])
-        _server.get_peer(id).put_packet(pkt)
+    func _ready():
+        # Start listening on the given port.
+        var err = _tcp_server.listen(PORT)
+        if err == OK:
+            print("Server started.")
+        else:
+            push_error("Unable to start server.")
+            set_process(false)
 
-    func _process(delta):
-        # Call this in _process or _physics_process.
-        # Data transfer, and signals emission will only happen when calling this function.
-        _server.poll()
 
-This will print (when a client connects) something similar to this:
+    func _process(_delta):
+        while _tcp_server.is_connection_available():
+            last_peer_id += 1
+            print("+ Peer %d connected." % last_peer_id)
+            var ws = WebSocketPeer.new()
+            ws.accept_stream(_tcp_server.take_connection())
+            _peers[last_peer_id] = ws
+
+        # Iterate over all connected peers using "keys()" so we can erase in the loop
+        for peer_id in _peers.keys():
+            var peer = _peers[peer_id]
+
+            peer.poll()
+
+            var peer_state = peer.get_ready_state()
+            if peer_state == WebSocketPeer.STATE_OPEN:
+                while peer.get_available_packet_count():
+                    var packet = peer.get_packet()
+                    if peer.was_string_packet():
+                        var packet_text = packet.get_string_from_utf8()
+                        print("< Got text data from peer %d: %s ... echoing" % [peer_id, packet_text])
+                        # Echo the packet back.
+                        peer.send_text(packet_text)
+                    else:
+                        print("< Got binary data from peer %d: %d ... echoing" % [peer_id, packet.size()])
+                        # Echo the packet back.
+                        peer.send(packet)
+            elif peer_state == WebSocketPeer.STATE_CLOSED:
+                # Remove the disconnected peer.
+                _peers.erase(peer_id)
+                var code = peer.get_close_code()
+                var reason = peer.get_close_reason()
+                print("- Peer %s closed with code: %d, reason %s. Clean: %s" % [peer_id, code, reason, code != -1])
+
+
+When a client connects, this will print something similar to this:
+
+.. highlight:: none
 
 ::
 
-    Client 1348090059 connected with protocol: selected-protocol
-    Got data from client 1348090059: Test packet ... echoing
+    Server started.
+    + Peer 2 connected.
+    < Got text data from peer 2: Test packet ... echoing
 
 Advanced chat demo
 ~~~~~~~~~~~~~~~~~~