mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-11-07 17:36:07 +00:00
Refactoring to asynchronous. (partially completed)
This commit is contained in:
parent
7ee08092d4
commit
096ea0c70c
72 changed files with 6033 additions and 5080 deletions
157
MinecraftClient/Protocol/PacketPipeline/SocketWrapper.cs
Normal file
157
MinecraftClient/Protocol/PacketPipeline/SocketWrapper.cs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Crypto;
|
||||
|
||||
namespace MinecraftClient.Protocol.PacketPipeline
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper for handling unencrypted & encrypted socket
|
||||
/// </summary>
|
||||
class SocketWrapper
|
||||
{
|
||||
private TcpClient tcpClient;
|
||||
|
||||
private AesStream? AesStream;
|
||||
|
||||
private PacketStream? packetStream = null;
|
||||
|
||||
private Stream ReadStream, WriteStream;
|
||||
|
||||
private bool Encrypted = false;
|
||||
|
||||
public int CompressionThreshold { get; set; } = 0;
|
||||
|
||||
|
||||
private SemaphoreSlim SendSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
private Task LastSendTask = Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new SocketWrapper
|
||||
/// </summary>
|
||||
/// <param name="client">TcpClient connected to the server</param>
|
||||
public SocketWrapper(TcpClient client)
|
||||
{
|
||||
tcpClient = client;
|
||||
ReadStream = WriteStream = client.GetStream();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the socket is still connected
|
||||
/// </summary>
|
||||
/// <returns>TRUE if still connected</returns>
|
||||
/// <remarks>Silently dropped connection can only be detected by attempting to read/write data</remarks>
|
||||
public bool IsConnected()
|
||||
{
|
||||
return tcpClient.Client != null && tcpClient.Connected;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the socket has data available to read
|
||||
/// </summary>
|
||||
/// <returns>TRUE if data is available to read</returns>
|
||||
public bool HasDataAvailable()
|
||||
{
|
||||
return tcpClient.Client.Available > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Switch network reading/writing to an encrypted stream
|
||||
/// </summary>
|
||||
/// <param name="secretKey">AES secret key</param>
|
||||
public void SwitchToEncrypted(byte[] secretKey)
|
||||
{
|
||||
if (Encrypted)
|
||||
throw new InvalidOperationException("Stream is already encrypted!?");
|
||||
Encrypted = true;
|
||||
ReadStream = WriteStream = AesStream = new AesStream(tcpClient.Client, secretKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send raw data to the server.
|
||||
/// </summary>
|
||||
/// <param name="buffer">data to send</param>
|
||||
public async Task SendAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await SendSemaphore.WaitAsync();
|
||||
await LastSendTask;
|
||||
LastSendTask = WriteStream.WriteAsync(buffer, cancellationToken).AsTask();
|
||||
SendSemaphore.Release();
|
||||
}
|
||||
|
||||
public async Task<Tuple<int, PacketStream>> GetNextPacket(bool handleCompress, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// ConsoleIO.WriteLine("GetNextPacket");
|
||||
if (packetStream != null)
|
||||
{
|
||||
await packetStream.DisposeAsync();
|
||||
packetStream = null;
|
||||
}
|
||||
|
||||
int readed = 0;
|
||||
(int packetSize, _) = await ReceiveVarIntRaw(ReadStream, cancellationToken);
|
||||
|
||||
int packetID;
|
||||
if (handleCompress && CompressionThreshold > 0)
|
||||
{
|
||||
(int sizeUncompressed, readed) = await ReceiveVarIntRaw(ReadStream, cancellationToken);
|
||||
if (sizeUncompressed != 0)
|
||||
{
|
||||
ZlibBaseStream zlibBaseStream = new(AesStream ?? ReadStream, packetSize: packetSize - readed);
|
||||
ZLibStream zlibStream = new(zlibBaseStream, CompressionMode.Decompress, leaveOpen: false);
|
||||
|
||||
zlibBaseStream.BufferSize = 16;
|
||||
(packetID, readed) = await ReceiveVarIntRaw(zlibStream, cancellationToken);
|
||||
zlibBaseStream.BufferSize = 512;
|
||||
|
||||
// ConsoleIO.WriteLine("packetID = " + packetID + ", readed = " + zlibBaseStream.packetReaded + ", size = " + packetSize + " -> " + sizeUncompressed);
|
||||
|
||||
packetStream = new(zlibStream, sizeUncompressed - readed, cancellationToken);
|
||||
|
||||
return new(packetID, packetStream);
|
||||
}
|
||||
}
|
||||
|
||||
(packetID, int readed2) = await ReceiveVarIntRaw(ReadStream, cancellationToken);
|
||||
|
||||
packetStream = new(AesStream ?? ReadStream, packetSize - readed - readed2, cancellationToken);
|
||||
|
||||
return new(packetID, packetStream);
|
||||
}
|
||||
|
||||
private async Task<Tuple<int, int>> ReceiveVarIntRaw(Stream stream, CancellationToken cancellationToken = default)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
byte[] b = new byte[1];
|
||||
while (true)
|
||||
{
|
||||
await stream.ReadAsync(b);
|
||||
i |= (b[0] & 0x7F) << j++ * 7;
|
||||
if (j > 5) throw new OverflowException("VarInt too big");
|
||||
if ((b[0] & 0x80) != 128) break;
|
||||
}
|
||||
return new(i, j);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the server
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
tcpClient.Close();
|
||||
}
|
||||
catch (SocketException) { }
|
||||
catch (IOException) { }
|
||||
catch (NullReferenceException) { }
|
||||
catch (ObjectDisposedException) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue