예제 #1
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);
            }
        }
예제 #2
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();
            }
        }
예제 #3
0
        public static ReceiveGatewayData CreateJson(SocketData data)
        {
            ReceiveGatewayData res;

            if (data.MessageType == WebSocketMessageType.Close)
            {
                res = null;
            }
            else
            {
                res = new ReceiveGatewayData();
                var j = JsonConvert.DeserializeObject <GatewayJson>(data.GetText());
                res.OpCode = j.op;
                // ReSharper disable once SwitchStatementMissingSomeCases (default case exists)
                switch (res.OpCode)
                {
                case GatewayOpCode.Dispatch:
                    res.Sequence  = (int)j.s;
                    res.EventName = j.t;
                    res.Data      = GatewayEvent.Create(j.t, j.d);
                    break;

                case GatewayOpCode.Heartbeat:
                    res.Data = ((JValue)j.d).Value;
                    break;

                case GatewayOpCode.Hello:
                    res.Data = new GatewayHelloData((JObject)j.d);
                    break;

                case GatewayOpCode.InvalidSession:
                    res.Data = (bool)((JValue)j.d).Value;
                    break;

                case GatewayOpCode.Reconnect:
                case GatewayOpCode.HeartbeatAck:
                    res.Data = null;
                    break;

                default:
                    throw new UnknownOpCodeException("Gateway op code " + (int)j.op + " is either invalid in a recieve context or is undefined.");
                }
            }
            return(res);
        }
예제 #4
0
        public void Start(ReceiveGatewayData helloData, bool reconnect)
        {
            Thread starter = DiscordEnvironment.CreateSubthread(() => {
                if (reconnect)
                {
                    _heartbeat.Stop();
                    _heartbeat.Dispose();
                }
                _fire = true;
                //Task initBeat = HeartbeatAsync();
                _heartbeat = new Timer(((GatewayHelloData)helloData.Data).HeartbeatInterval.TotalMilliseconds)
                {
                    AutoReset           = true,
                    SynchronizingObject = null
                };
                _heartbeat.Elapsed += (sender, args) => HeartbeatAsync().Await();
                _heartbeat.Start();
                //initBeat.Await();
                _ackMutex.Wait();
                if (_aborts > 0)
                {
                    _fire = false;
                    _aborts--;
                    _ackMutex.Release();
                }
                else
                {
                    bool oldFire = _fire;
                    _fire        = false;
                    _ackMutex.Release();
                    if (oldFire)
                    {
                        Started?.Invoke(this, true);
                    }
                }
            });

            starter.Start();
        }
예제 #5
0
 public static bool EventIsThis(ReceiveGatewayData data)
 {
     return(data.EventName != null && EventIsThis(data.EventName));
 }
예제 #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);
        }