Ejemplo n.º 1
0
        public override string ToString()
        {
            string res = "[[[\nOp " + OpCode + " (" + (int)OpCode + ")";

            if (EventName != null)
            {
                res += "\nSeq " + Sequence + "\nEvt " + EventName;
            }
            res += "\nData " + DiscordDebug.JsonString(Data) + "\n]]]";
            return(res);
        }
Ejemplo n.º 2
0
        private async Task <Server[]> HandshakeAsync(bool reconnect, bool resumable, GatewaySocket socket, IRef <bool> loop)
        {
            DiscordDebug.WriteLine("Getting Hello data...");
            ReceiveGatewayData data = await socket.ReadAsync();

            DiscordDebug.WriteLine("Got Hello data.");
            if (reconnect)
            {
                _listener.UpdateSocket(socket);
            }
            else
            {
                DiscordDebug.WriteLine("Initializing response listener and heartbeater...");
                _listener    = new ResponseListener(socket);
                _heartbeater = new GatewayHeartbeater(_listener, _seqMutex, () => _lastSequence, ConnectAsync);
                DiscordDebug.WriteLine("Response listener and heartbeater initialized.");
            }
            // Need to handle cases where heartbeat timer elapses, thus the event/semaphore model
            DiscordDebug.WriteLine("Waiting for initial heartbeat ack...");
            var  signal  = new Semaphore(0, 1);
            bool success = false;

            void EvtListener(object sender, bool e)
            {
                success = e;
                signal.Release();
            }

            _heartbeater.Started += EvtListener;
            _heartbeater.Start(data, reconnect);
            signal.WaitOne();
            _heartbeater.Started -= EvtListener;
            if (success)
            {
                DiscordDebug.WriteLine("Heartbeat ack heard.");
                return(await IdentAsync(reconnect, resumable));

                // ReSharper disable once RedundantIfElseBlock
            }
            else
            {
                DiscordDebug.WriteLine("Heartbeat ack not heard, trying again...");
                // Reconnect immediately
                await _heartbeater.AbortAsync();

                _heartbeater.Dispose();
                _listener.Dispose();
                loop.Value = true;
                return(null);
            }
        }
Ejemplo n.º 3
0
        private void GuildCreate(ReceiveGatewayData data)
        {
            var d = (GatewayEvent.EvtServer)data.Data;

            _asMutex.Wait();
            try {
                DiscordDebug.WriteLine("GUILD_CREATE " + d.Data.Id + " (" + (_awaitServers.Count - 1) + " remaining)");
                _awaitServers.Remove(d.Data.Id);
                if (_awaitServers.Count == 0)
                {
                    _asDone.Release();
                }
            } finally {
                _asMutex.Release();
            }
        }
Ejemplo n.º 4
0
        internal async Task <Server[]> ConnectAsync(bool reconnect, bool resumable)
        {
            if (reconnect)
            {
                _dispatcher?.TearDown();
            }
            var loop  = new SharedRef <bool>(false);
            int loops = 0;

            DiscordDebug.WriteLine("Starting connection appempt...");
            Server[] res = null;
            do
            {
                if (loops >= _maxConnectAttempts)
                {
                    throw new GatewayConnectException("Gateway failed to connect " + _maxConnectAttempts + " times, and the connection attempt was aborted.");
                }
                loop.Value = false;
                await _connectMutex.WaitAsync();

                if (_listener == null)
                {
                    string url = await GetGatewayUrlAsync();

                    DiscordDebug.WriteLine("Opening socket...");
                    var socket = new GatewaySocket(this);
                    socket.Connect(url);
                    if (socket.State == WebSocketState.Open)
                    {
                        DiscordDebug.WriteLine("Socket opened.");
                        res = await HandshakeAsync(reconnect, resumable, socket, loop);
                    }
                    else
                    {
                        DiscordDebug.WriteLine("Socket opening failed, trying again...");
                        loop.Value = true;
                    }
                }
                _connectMutex.Release();
                loops++;
            } while (loop.Value);
            DiscordDebug.WriteLine("Connection attempt done.");
            // ReSharper disable once AssignNullToNotNullAttribute (null value is default to get compiler to shut up - whenever res is null, the loop should continue)
            return(res);
        }
Ejemplo n.º 5
0
        public static object Create(string eventName, object data)
        {
            object res;

            if (Ready.EventIsThis(eventName))
            {
                res = new Ready((JObject)data);
            }
            else if (Resumed.EventIsThis(eventName))
            {
                res = new Resumed((JObject)data);
            }
            else if (InvalidSession.EventIsThis(eventName))
            {
                res = (bool)((JValue)data).Value;
            }
            else if (Channel.EventIsThis(eventName))
            {
                res = new Channel((JObject)data);
            }
            else if (ChannelPinsUpdate.EventIsThis(eventName))
            {
                res = new ChannelPinsUpdate((JObject)data);
            }
            else if (EvtServer.EventIsThis(eventName))
            {
                res = new EvtServer((JObject)data);
            }
            else if (ServerDelete.EventIsThis(eventName))
            {
                res = ((JObject)data).ToObject <UaServerJson>().id;
            }
            else if (ServerUser.EventIsThis(eventName))
            {
                res = new ServerUser((JObject)data, eventName == ServerUser.MemberRemoveId);
            }
            else if (ServerUserSet.EventIsThis(eventName))
            {
                res = new ServerUserSet((JObject)data);
            }
            else if (ServerUserUpdate.EventIsThis(eventName))
            {
                res = new ServerUserUpdate((JObject)data);
            }
            else if (ServerEmojiUpdate.EventIsThis(eventName))
            {
                res = new ServerEmojiUpdate((JObject)data);
            }
            else if (RawServer.EventIsThis(eventName))
            {
                res = new RawServer((JObject)data);
            }
            else if (ServerRole.EventIsThis(eventName))
            {
                res = new ServerRole((JObject)data);
            }
            else if (ServerRoleDelete.EventIsThis(eventName))
            {
                res = new ServerRoleDelete((JObject)data);
            }
            else if (Message.EventIsThis(eventName))
            {
                res = new Message((JObject)data, eventName == Message.UpdateId);
            }
            else if (RawMessage.EventIsThis(eventName))
            {
                res = new RawMessage((JObject)data, eventName == RawMessage.BulkDeleteId);
            }
            else if (Reaction.EventIsThis(eventName))
            {
                res = new Reaction((JObject)data);
            }
            else if (ReactionRemoveAll.EventIsThis(eventName))
            {
                res = new ReactionRemoveAll((JObject)data);
            }
            else if (Webhook.EventIsThis(eventName))
            {
                res = new Webhook((JObject)data);
            }
            else if (EvtUser.EventIsThis(eventName))
            {
                res = new EvtUser((JObject)data);
            }
            else if (Unimplemented.EventIsThis(eventName))
            {
                res = Unimplemented.Instance;
            }
            else
            {
                throw new UnknownEventException("Gateway event \"" + eventName + "\" is unknown.");
            }
            DiscordDebug.WriteLine("Heard event: " + eventName + ", handling w/ object " + res.GetType().Name);
            return(res);
        }
Ejemplo n.º 6
0
        private async Task <Server[]> IdentAsync(bool reconnect, bool resumable)
        {
            bool ident = true;

            if (reconnect && resumable)
            {
                ReceiveGatewayData resumed = await _listener.SendAsync(SendGatewayData.Create(GatewayOpCode.Resume, MakeResume()), new[] {
                    GatewayOpCode.Dispatch,
                    GatewayOpCode.InvalidSession
                }, GatewayEvent.Resumed.Id);

                ident = resumed.OpCode == GatewayOpCode.InvalidSession;
                if (ident)
                {
                    // Per specs, client must wait 1-5 seconds before sending a fresh Identify payload (https://discordapp.com/developers/docs/topics/gateway#resuming)
                    await Task.Delay(2000);
                }
            }
            await _asMutex.WaitAsync();

            bool needRelease = true;

            Server[] res = null;
            try {
                while (ident)
                {
                    DiscordDebug.WriteLine("Identing...");
                    ReceiveGatewayData ready = await _listener.SendAsync(SendGatewayData.Create(GatewayOpCode.Identify, (JObject)MakeIdentify()), new[] {
                        GatewayOpCode.Dispatch,
                        GatewayOpCode.InvalidSession
                    }, GatewayEvent.Ready.Id);

                    ident = ready.OpCode == GatewayOpCode.InvalidSession;
                    if (ident)
                    {
                        DiscordDebug.WriteLine("Ident rate-limited, waiting and trying again...");
                        // Per specs, the only case in which this occurs is when you hit the 5-second Identify rate limit (https://discordapp.com/developers/docs/topics/gateway#identifying)
                        await Task.Delay(5000);
                    }
                    else
                    {
                        DiscordDebug.WriteLine("Ident successful, copying session data...");
                        var readyData = (GatewayEvent.Ready)ready.Data;
                        _sessionId = readyData.SessionId;
                        DiscordDebug.WriteLine("Session data copied.");
                        DiscordDebug.WriteLine("Awaiting server create events (expecting [" + string.Join(", ", readyData.ServersToCreate) + "] (" + readyData.ServersToCreate.Count + "))...");
                        _awaitServers = readyData.ServersToCreate;
                        _listener.Listen(GuildCreate, _awaitServers.Count, GatewayOpCode.Dispatch, GatewayEvent.EvtServer.CreateId);
                        _asDone = new Semaphore(0, 1);
                        _asMutex.Release();
                        needRelease = false;
                        res         = readyData.ServersToCreate.Select(Server.GetCached).ToArray();
                        _asDone.WaitOne();
                        DiscordDebug.WriteLine("All expected server create events have occured.");
                    }
                    DiscordDebug.WriteLine("Ident done.");
                }
            } finally {
                if (needRelease)
                {
                    _asMutex.Release();
                }
            }
            DiscordDebug.WriteLine("Setting up event listener...");
            _dispatcher = new Dispatcher(this, _listener);
            DiscordDebug.WriteLine("Event listener set up.");
            DiscordDebug.WriteLine("Socket ready to use");
            // ReSharper disable once AssignNullToNotNullAttribute
            return(res);
        }