From e3922e24d96cd10b2e20b694dc657331980d5fab Mon Sep 17 00:00:00 2001
From: Rapptz <rapptz@gmail.com>
Date: Wed, 12 Aug 2020 22:16:39 -0400
Subject: [PATCH] Correct some protocol errors in v4 of voice gateway

---
 discord/gateway.py      | 17 ++++++++---------
 discord/voice_client.py | 10 +++++++---
 2 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/discord/gateway.py b/discord/gateway.py
index 81ff69b8c..382d60f1b 100644
--- a/discord/gateway.py
+++ b/discord/gateway.py
@@ -635,8 +635,8 @@ class DiscordVoiceWebSocket:
         Sent only. Tells the client to resume its session.
     HELLO
         Receive only. Tells you that your websocket connection was acknowledged.
-    INVALIDATE_SESSION
-        Sent only. Tells you that your RESUME request has failed and to re-IDENTIFY.
+    RESUMED
+        Sent only. Tells you that your RESUME request has succeeded.
     CLIENT_CONNECT
         Indicates a user has connected to voice.
     CLIENT_DISCONNECT
@@ -652,7 +652,7 @@ class DiscordVoiceWebSocket:
     HEARTBEAT_ACK       = 6
     RESUME              = 7
     HELLO               = 8
-    INVALIDATE_SESSION  = 9
+    RESUMED             = 9
     CLIENT_CONNECT      = 12
     CLIENT_DISCONNECT   = 13
 
@@ -755,9 +755,8 @@ class DiscordVoiceWebSocket:
             await self.initial_connection(data)
         elif op == self.HEARTBEAT_ACK:
             self._keep_alive.ack()
-        elif op == self.INVALIDATE_SESSION:
-            log.info('Voice RESUME failed.')
-            await self.identify()
+        elif op == self.RESUMED:
+            log.info('Voice RESUME succeeded.')
         elif op == self.SESSION_DESCRIPTION:
             self._connection.mode = data['mode']
             await self.load_secret_key(data)
@@ -773,7 +772,9 @@ class DiscordVoiceWebSocket:
         state.endpoint_ip = data['ip']
 
         packet = bytearray(70)
-        struct.pack_into('>I', packet, 0, state.ssrc)
+        struct.pack_into('>H', packet, 0, 1) # 1 = Send
+        struct.pack_into('>H', packet, 2, 70) # 70 = Length
+        struct.pack_into('>I', packet, 4, state.ssrc)
         state.socket.sendto(packet, (state.endpoint_ip, state.voice_port))
         recv = await self.loop.sock_recv(state.socket, 70)
         log.debug('received packet in initial_connection: %s', recv)
@@ -794,8 +795,6 @@ class DiscordVoiceWebSocket:
         await self.select_protocol(state.ip, state.port, mode)
         log.info('selected the voice protocol for use (%s)', mode)
 
-        await self.client_connect()
-
     @property
     def latency(self):
         """:class:`float`: Latency between a HEARTBEAT and its HEARTBEAT_ACK in seconds."""
diff --git a/discord/voice_client.py b/discord/voice_client.py
index a1a7109a4..58cb81c49 100644
--- a/discord/voice_client.py
+++ b/discord/voice_client.py
@@ -121,10 +121,14 @@ class VoiceProtocol:
 
         An abstract method called when the client initiates the connection request.
 
-        When a connection is requested initially, the library calls the following functions
-        in order:
+        When a connection is requested initially, the library calls the constructor
+        under ``__init__`` and then calls :meth:`connect`. If :meth:`connect` fails at
+        some point then :meth:`disconnect` is called.
 
-        - ``__init__``
+        Within this method, to start the voice connection flow it is recommended to
+        use :meth:`Guild.change_voice_state` to start the flow. After which,
+        :meth:`on_voice_server_update` and :meth:`on_voice_state_update` will be called.
+        The order that these two are called is unspecified.
 
         Parameters
         ------------