/// <summary> /// Event called whenever raw data is received on the TCP socket. /// </summary> private void TCPPacketReceived(object sender, SocketAsyncEventArgs e) { // Parse out the actual RCON packet RCONPacket packet = RCONPacket.FromBytes(e.Buffer); if (packet.Type == PacketType.AuthResponse) { // Failed auth responses return with an ID of -1 if (packet.Id == -1) { throw new AuthenticationException($"Authentication failed for {_tcp.RemoteEndPoint}."); } // Tell Connect that authentication succeeded _authenticationTask.SetResult(true); } // Forward to handler RCONPacketReceived(packet); // Continue listening if (!_connected) { return; } _tcp.ReceiveAsync(e); }
/// <summary> /// Merges RCON packet bodies and resolves the waiting task /// with the full body when full response has been recived. /// </summary> /// <param name="packet"> Newly received packet </param> private void RCONPacketReceived(RCONPacket packet) { // Call pending result and remove from map TaskCompletionSource <string> taskSource; if (_pendingCommands.TryGetValue(packet.Id, out taskSource)) { if (_multiPacket) { //Read any previous messages string body; _incomingBuffer.TryGetValue(packet.Id, out body); if (packet.Body == "") { //Avoid yielding taskSource.SetResult(body ?? string.Empty); _pendingCommands.Remove(packet.Id); _incomingBuffer.Remove(packet.Id); } else { //Append to previous messages _incomingBuffer[packet.Id] = body + packet.Body; } } else { //Avoid yielding taskSource.SetResult(packet.Body); _pendingCommands.Remove(packet.Id); } } }
/// <summary> /// Send a command to the server, and wait for the response before proceeding. R /// </summary> /// <param name="command">Command to send to the server.</param> /// <exception cref = "System.AggregateException" >Connection exceptions</ exception > public async Task <string> SendCommandAsync(string command) { Monitor.Enter(_lock); // This TaskCompletion source could be initialized with TaskCreationOptions.RunContinuationsAsynchronously // However we this library is designed to be able to run without its own thread // Read more about this option here: // https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#always-create-taskcompletionsourcet-with-taskcreationoptionsruncontinuationsasynchronously var source = new TaskCompletionSource <string>(); _pendingCommands.Add(++_packetId, source); var packet = new RCONPacket(_packetId, PacketType.ExecCommand, command); Monitor.Exit(_lock); await SendPacketAsync(packet); await Task.WhenAny(source.Task, _networkConsumerTask); if (source.Task.IsCompleted) { return(source.Task.Result); } if (_networkConsumerTask.IsFaulted) { throw _networkConsumerTask.Exception.InnerException; } else { throw new SocketException(); } }
/// <summary> /// Send a packet to the server. /// </summary> /// <param name="packet">Packet to send, which will be serialized.</param> private async Task SendPacketAsync(RCONPacket packet) { if (!_connected) { throw new InvalidOperationException("Connection is closed."); } await _tcp.SendAsync(new ArraySegment <byte>(packet.ToBytes()), SocketFlags.None); }
/// <summary> /// Attempt to parse the line, and call the callback if it succeeded. /// </summary> /// <param name="line">Single line from the server.</param> internal void TryCallback(RCONPacket line) { object result; if (TryParse(line, out result)) { Callback(result); } }
/// <summary> /// Attempt to parse the line into an object. /// </summary> /// <param name="line">Single line from the server.</param> /// <param name="result">Object to parse into.</param> /// <returns>False if the match failed or parsing was unsuccessful.</returns> private bool TryParse(RCONPacket line, out object result) { if (!IsMatch(line)) { result = null; return(false); } result = Parse(line); return(result != null); }
private void RCONPacketReceived(RCONPacket packet) { // Call pending result and remove from map Action <RCONPacket> action; if (_pendingCommands.TryGetValue(packet.Id, out action)) { action?.Invoke(packet); _pendingCommands.Remove(packet.Id); } }
/// <summary> /// Event called whenever raw data is received on the TCP socket. /// </summary> private void TCPPacketReceived(object sender, SocketAsyncEventArgs e) { // Parse out the actual RCON packet RCONPacket packet = null; try { packet = RCONPacket.FromBytes(e.Buffer); } catch (Exception exception) { Console.WriteLine("Unable to read packet, this has been observed when trying to communicate" + " with a server that is currently booting.\n" + exception.Message); Dispose(); return; } if (packet.Type == PacketType.AuthResponse) { // Failed auth responses return with an ID of -1 if (packet.Id == -1) { Console.WriteLine($"Authentication failed for {_tcp.RemoteEndPoint}."); Dispose(); return; } // Tell Connect that authentication succeeded try { _authenticationTask.SetResult(true); } catch { Console.WriteLine("Failure setting authentication task - disposing"); Dispose(); return; } } // Forward to handler RCONPacketReceived(packet); // Continue listening if (!_connected) { return; } _tcp.ReceiveAsync(e); }
/// <summary> /// Send a command to the server, and wait for the response before proceeding. /// </summary> /// <param name="command">Command to send to the server.</param> public async Task <byte[]> SendCommandAsync(byte[] command) { Monitor.Enter(_lock); var source = new TaskCompletionSource <RCONPacket>(); _pendingCommands.Add(++_packetId, source.SetResult); var packet = new RCONPacket(_packetId, PacketType.ExecCommand, command); Monitor.Exit(_lock); await SendPacketAsync(packet); return((await source.Task).RawBody); }
/// <summary> /// Send a packet to the server. /// </summary> /// <param name="packet">Packet to send, which will be serialized.</param> private async Task SendPacketAsync(RCONPacket packet) { if (!_connected) { throw new InvalidOperationException("Connection is closed."); } await _tcp.SendAsync(new ArraySegment <byte>(packet.ToBytes()), SocketFlags.None); if (packet.Type == PacketType.ExecCommand && _multiPacket) { //Send a extra packet to find end of large packets await _tcp.SendAsync(new ArraySegment <byte>(new RCONPacket(packet.Id, PacketType.Response, "").ToBytes()), SocketFlags.None); } }
/// <summary> /// Event called whenever raw data is received on the TCP socket. /// </summary> private void TCPPacketReceived(object sender, SocketAsyncEventArgs e) { int size = BitConverter.ToInt32(e.Buffer, 0) + 4; if (size > e.Buffer.Length) { throw new OverflowException($"RCON message {size} bytes is larger than TCP buffer {e.Buffer.Length} bytes"); } if (e.BytesTransferred + e.Offset < size) { // need more data, put e back for another go... e.SetBuffer(e.Offset + e.BytesTransferred, size - (e.Offset + e.BytesTransferred)); if (!_connected) { return; } _tcp.ReceiveAsync(e); } else { // Parse out the actual RCON packet RCONPacket packet = RCONPacket.FromBytes(e.Buffer); if (packet.Type == PacketType.AuthResponse) { // Failed auth responses return with an ID of -1 if (packet.Id == -1) { throw new AuthenticationException($"Authentication failed for {_tcp.RemoteEndPoint}."); } // Tell Connect that authentication succeeded _authenticationTask.SetResult(true); } // Forward to handler RCONPacketReceived(packet); // Continue listening if (!_connected) { return; } e.SetBuffer(0, 4); _tcp.ReceiveAsync(e); } }
/// <summary> /// Send a command to the server, and wait for the response before proceeding. R /// </summary> /// <param name="command">Command to send to the server.</param> public async Task <string> SendCommandAsync(string command) { Monitor.Enter(_lock); var source = new TaskCompletionSource <string>(); _pendingCommands.Add(++_packetId, source.SetResult); var packet = new RCONPacket(_packetId, PacketType.ExecCommand, command); Monitor.Exit(_lock); _staleCounter = 0; await SendPacketAsync(packet); return(await source.Task); }
public override SteamPacket GetReply() { if (this.ReceivePacket(1440) <= 0) { throw new RCONBanException(); } int packetSize = this.bufferReader.ReadInt32() + 4; if (packetSize > 1440) { throw new Exception("Invalid packet size"); } return(RCONPacket.CreatePacket(buffer)); }
private void OnClientMessage(byte[] message) { // Read RCON message RCONPacket packet = RCONPacket.ReadData(message); // Check if packet is server response (0) or invalid // RCON auth response (-1) if (packet.ID != 0 && packet.ID != -1) return; switch (packet.Type) { case RCONPacketType.AUTH_RESPONSE: { // Read packet as AuthResponse RCONAuthResponsePacket authPacket = packet as RCONAuthResponsePacket; if (!authPacket.WasSuccessful()) { OutputFunction.Invoke("Failed to connect: incorrect password."); break; } OutputFunction.Invoke("Successfully connected!"); break; } case RCONPacketType.RESPONSE_VALUE: { if (string.IsNullOrEmpty(packet.Body)) break; OutputFunction.Invoke(packet.Body); break; } case RCONPacketType.DATA: { if (string.IsNullOrEmpty(packet.Body)) break; OutputFunction.Invoke(packet.Body); break; } default: { OutputFunction.Invoke($"Received uncaught packet of type {packet.Type}"); break; } } packet.Dispose(); }
protected bool SyncSend(RCONPacket p) { byte[] Packets = p.OutputAsBytes(); checked { if (S.Connected) { if (S.Send(Packets, 0, Packets.Length, SocketFlags.None) == Packets.Length) { Thread.Sleep(1000); int iCount = 0; while (S.Available < 4 & iCount < 5) { Thread.Sleep(1000); iCount++; } PacketsReceived = new byte[S.Available - 1 + 1]; int iIndex = 0; while (S.Available > 0) { int iRec = S.Receive(PacketsReceived, iIndex, S.Available, SocketFlags.None); iIndex += iRec; } return(iIndex > 0); } } else if (!S.Connected & m_connected) { m_connected = false; } return(false); } }
/// <summary> /// Send a command to the server, and wait for the response before proceeding. Expect the result to be parseable into T. /// </summary> /// <typeparam name="T">Type to parse the command as.</typeparam> /// <param name="command">Command to send to the server.</param> public async Task <T> SendCommandAsync <T>(string command) where T : class, IParseable, new() { Monitor.Enter(_lock); var source = new TaskCompletionSource <T>(); var instance = ParserHelpers.CreateParser <T>(); var container = new ParserContainer { IsMatch = line => instance.IsMatch(line.Body), Parse = line => instance.Parse(line.Body), Callback = parsed => source.SetResult((T)parsed) }; _pendingCommands.Add(++_packetId, container.TryCallback); var packet = new RCONPacket(_packetId, PacketType.ExecCommand, command); Monitor.Exit(_lock); await SendPacketAsync(packet); return(await source.Task); }
public void Send(RCONPacket dataPacket) { byte[] byteData = dataPacket.GetBytes(true); this.client.Send(byteData, byteData.Length); }
/// <summary> /// Read data from pipeline when available, constructing new RCON packets /// </summary> /// <param name="reader"></param> /// <returns>Consumer Task</returns> async Task ReadPipeAsync(PipeReader reader) { byte[] byteArr = new byte[Constants.MAX_PACKET_SIZE]; while (true) { ReadResult result = await reader.ReadAsync(); ReadOnlySequence <byte> buffer = result.Buffer; SequencePosition packetStart = buffer.Start; if (buffer.Length < 4) { if (result.IsCompleted) { break; } reader.AdvanceTo(packetStart, buffer.End); continue; // Complete header not yet received } int size = BitConverter.ToInt32(buffer.Slice(packetStart, 4).ToArray(), 0); if (buffer.Length >= size + 4) { // Get packet end positions SequencePosition packetEnd = buffer.GetPosition(size + 4, packetStart); byteArr = buffer.Slice(packetStart, packetEnd).ToArray(); RCONPacket packet = RCONPacket.FromBytes(byteArr); if (packet.Type == PacketType.AuthResponse) { // Failed auth responses return with an ID of -1 if (packet.Id == -1) { _authenticationTask.SetException( new AuthenticationException($"Authentication failed for {_tcp.RemoteEndPoint}.") ); } // Tell Connect that authentication succeeded _authenticationTask.SetResult(true); } // Forward rcon packet to handler RCONPacketReceived(packet); reader.AdvanceTo(packetEnd); } else { reader.AdvanceTo(packetStart, buffer.End); } // Tell the PipeReader how much of the buffer we have consumed // Stop reading if there's no more data coming if (buffer.IsEmpty && result.IsCompleted) { break; // exit loop } } // If authentication did not complete _authenticationTask.TrySetException( new AuthenticationException($"Server did not respond to auth {_tcp.RemoteEndPoint}.") ); // Mark the PipeReader as complete await reader.CompleteAsync(); }