private async Task HeartbeatAsync() { await _ackMutex.WaitAsync(); bool needRelease = true; try { if (_heardAck) { _heardAck = false; } else { await _connect(true, true); _heardAck = false; bool oldFire = _fire; _fire = false; _ackMutex.Release(); needRelease = false; if (oldFire) { Started?.Invoke(this, false); } } } finally { if (needRelease) { _ackMutex.Release(); } } await _seqMutex.WaitAsync(); JObject obj; try { obj = SendGatewayData.Create(GatewayOpCode.Heartbeat, _seqGetter()); } finally { _seqMutex.Release(); } await _listener.SendAsync(obj, GatewayOpCode.HeartbeatAck); await _ackMutex.WaitAsync(); try { _heardAck = true; } finally { _ackMutex.Release(); } }
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); }