mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
Stage 1
This commit is contained in:
parent
1ca023be36
commit
3a4d8951d5
20 changed files with 1511 additions and 389 deletions
|
|
@ -2,6 +2,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Inventory.ItemPalettes;
|
||||
using MinecraftClient.Mapping;
|
||||
|
|
@ -313,6 +315,27 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an integer from the network asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>The integer</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public async Task<int> ReadNextVarIntRAWAsync(SocketWrapper socket, CancellationToken cancellationToken)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
byte b;
|
||||
while (true)
|
||||
{
|
||||
b = (await socket.ReadDataRAWAsync(1, cancellationToken))[0];
|
||||
i |= (b & 0x7F) << j++ * 7;
|
||||
if (j > 5) throw new OverflowException("VarInt too big");
|
||||
if ((b & 0x80) != 128) break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an integer from a cache of bytes and remove it from the cache
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Net.Sockets;
|
|||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Crypto;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Mapping;
|
||||
|
|
@ -29,7 +30,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
readonly IMinecraftComHandler handler;
|
||||
private bool encrypted = false;
|
||||
private readonly int protocolversion;
|
||||
private Tuple<Thread, CancellationTokenSource>? netRead = null;
|
||||
private Task? netReadTask;
|
||||
private CancellationTokenSource? netReadCancellationTokenSource;
|
||||
private int netReadThreadId = -1;
|
||||
Crypto.AesCfb8Stream? s;
|
||||
readonly TcpClient c;
|
||||
|
||||
|
|
@ -69,15 +72,15 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
c = Client;
|
||||
}
|
||||
|
||||
private void Updater(object? o)
|
||||
private void Updater(CancellationToken cancelToken)
|
||||
{
|
||||
var cancelToken = (CancellationToken)o!;
|
||||
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
netReadThreadId = Environment.CurrentManagedThreadId;
|
||||
using IDisposable _ = MainThreadExecutionScope.Enter(handler);
|
||||
Stopwatch stopWatch = Stopwatch.StartNew();
|
||||
long nextUpdateDue = 0;
|
||||
|
||||
|
|
@ -104,6 +107,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
catch (SocketException) { }
|
||||
catch (ObjectDisposedException) { }
|
||||
catch (OperationCanceledException) { }
|
||||
catch (Exception) { }
|
||||
finally { netReadThreadId = -1; }
|
||||
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
return;
|
||||
|
|
@ -240,9 +245,13 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
|
||||
private void StartUpdating()
|
||||
{
|
||||
netRead = new(new Thread(new ParameterizedThreadStart(Updater)), new CancellationTokenSource());
|
||||
netRead.Item1.Name = "ProtocolPacketHandler";
|
||||
netRead.Item1.Start(netRead.Item2.Token);
|
||||
CancellationTokenSource netReadCts = new();
|
||||
netReadCancellationTokenSource = netReadCts;
|
||||
netReadTask = Task.Factory.StartNew(
|
||||
() => Updater(netReadCts.Token),
|
||||
netReadCts.Token,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -251,7 +260,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <returns>Net read thread ID</returns>
|
||||
public int GetNetMainThreadId()
|
||||
{
|
||||
return netRead is not null ? netRead.Item1.ManagedThreadId : -1;
|
||||
return netReadThreadId;
|
||||
}
|
||||
|
||||
public bool SendCookieResponse(string name, byte[]? data)
|
||||
|
|
@ -268,9 +277,9 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
try
|
||||
{
|
||||
if (netRead is not null)
|
||||
if (netReadCancellationTokenSource is not null)
|
||||
{
|
||||
netRead.Item2.Cancel();
|
||||
netReadCancellationTokenSource.Cancel();
|
||||
c.Close();
|
||||
}
|
||||
}
|
||||
|
|
@ -519,7 +528,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
while (pid[0] == 0xFA) //Skip some early plugin messages
|
||||
{
|
||||
ProcessPacket(pid[0]);
|
||||
using (MainThreadExecutionScope.Enter(handler))
|
||||
ProcessPacket(pid[0]);
|
||||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
}
|
||||
if (pid[0] == 0xFD)
|
||||
|
|
@ -559,8 +569,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
if (session.ServerPublicKey is not null && session.SessionPreCheckTask is not null
|
||||
&& serverIDhash == session.ServerIDhash && Enumerable.SequenceEqual(serverPublicKey, session.ServerPublicKey))
|
||||
{
|
||||
session.SessionPreCheckTask.Wait();
|
||||
if (session.SessionPreCheckTask.Result) // PreCheck Successed
|
||||
if (session.SessionPreCheckTask.IsCompletedSuccessfully && session.SessionPreCheckTask.Result)
|
||||
needCheckSession = false;
|
||||
}
|
||||
|
||||
|
|
@ -633,7 +642,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
while (pid[0] >= 0xC0 && pid[0] != 0xFF) //Skip some early packets or plugin messages
|
||||
{
|
||||
ProcessPacket(pid[0]);
|
||||
using (MainThreadExecutionScope.Enter(handler))
|
||||
ProcessPacket(pid[0]);
|
||||
Receive(pid, 0, 1, SocketFlags.None);
|
||||
}
|
||||
if (pid[0] == (byte)1)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Security.Cryptography;
|
|||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Crypto;
|
||||
using MinecraftClient.Inventory;
|
||||
using MinecraftClient.Inventory.ItemPalettes;
|
||||
|
|
@ -117,8 +118,11 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
readonly PacketTypePalette packetPalette;
|
||||
readonly SocketWrapper socketWrapper;
|
||||
readonly DataTypes dataTypes;
|
||||
Tuple<Thread, CancellationTokenSource>? netMain = null; // main thread
|
||||
Tuple<Thread, CancellationTokenSource>? netReader = null; // reader thread
|
||||
private Task? netMainTask;
|
||||
private CancellationTokenSource? netMainCancellationTokenSource;
|
||||
private int netMainThreadId = -1;
|
||||
private Task? netReaderTask;
|
||||
private CancellationTokenSource? netReaderCancellationTokenSource;
|
||||
readonly ILogger log;
|
||||
readonly RandomNumberGenerator randomGen;
|
||||
private bool legacyAchievementsInitialized;
|
||||
|
|
@ -278,17 +282,17 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Separate thread. Network reading loop.
|
||||
/// Serialized packet/tick loop.
|
||||
/// </summary>
|
||||
private void Updater(object? o)
|
||||
private void Updater(CancellationToken cancelToken)
|
||||
{
|
||||
var cancelToken = (CancellationToken)o!;
|
||||
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
netMainThreadId = Environment.CurrentManagedThreadId;
|
||||
using IDisposable _ = MainThreadExecutionScope.Enter(handler);
|
||||
Stopwatch stopWatch = Stopwatch.StartNew();
|
||||
long nextUpdateDue = 0;
|
||||
while (!packetQueue.IsAddingCompleted)
|
||||
|
|
@ -330,6 +334,13 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
catch (System.IO.IOException)
|
||||
{
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
netMainThreadId = -1;
|
||||
}
|
||||
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
return;
|
||||
|
|
@ -340,20 +351,13 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <summary>
|
||||
/// Read and decompress packets.
|
||||
/// </summary>
|
||||
internal void PacketReader(object? o)
|
||||
internal async Task PacketReaderAsync(CancellationToken cancelToken)
|
||||
{
|
||||
var cancelToken = (CancellationToken)o!;
|
||||
while (socketWrapper.IsConnected() && !cancelToken.IsCancellationRequested)
|
||||
while (!cancelToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (socketWrapper.HasDataAvailable())
|
||||
{
|
||||
packetQueue.Add(ReadNextPacket(), cancelToken);
|
||||
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
break;
|
||||
}
|
||||
packetQueue.Add(await ReadNextPacketAsync(cancelToken), cancelToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
|
@ -375,11 +379,10 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (cancelToken.IsCancellationRequested)
|
||||
catch (Exception)
|
||||
{
|
||||
break;
|
||||
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
packetQueue.CompleteAdding();
|
||||
|
|
@ -415,6 +418,30 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return new(packetId, packetData);
|
||||
}
|
||||
|
||||
internal async Task<Tuple<int, Queue<byte>>> ReadNextPacketAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var size = await dataTypes.ReadNextVarIntRAWAsync(socketWrapper, cancellationToken); //Packet size
|
||||
Queue<byte> packetData = new(await socketWrapper.ReadDataRAWAsync(size, cancellationToken)); //Packet contents
|
||||
|
||||
if (protocolVersion >= MC_1_8_Version
|
||||
&& compression_treshold >= 0)
|
||||
{
|
||||
var sizeUncompressed = dataTypes.ReadNextVarInt(packetData);
|
||||
if (sizeUncompressed != 0)
|
||||
{
|
||||
var toDecompress = packetData.ToArray();
|
||||
var uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed);
|
||||
packetData = new Queue<byte>(uncompressed);
|
||||
}
|
||||
}
|
||||
|
||||
var packetId = dataTypes.ReadNextVarInt(packetData);
|
||||
if (handler.GetNetworkPacketCaptureEnabled())
|
||||
handler.OnNetworkPacket(packetId, packetData.ToList(), currentState == CurrentState.Login, true);
|
||||
|
||||
return new(packetId, packetData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the given packet
|
||||
/// </summary>
|
||||
|
|
@ -3844,19 +3871,17 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// </summary>
|
||||
private void StartUpdating()
|
||||
{
|
||||
Thread threadUpdater = new(new ParameterizedThreadStart(Updater))
|
||||
{
|
||||
Name = "ProtocolPacketHandler"
|
||||
};
|
||||
netMain = new Tuple<Thread, CancellationTokenSource>(threadUpdater, new CancellationTokenSource());
|
||||
threadUpdater.Start(netMain.Item2.Token);
|
||||
CancellationTokenSource netMainCts = new();
|
||||
netMainCancellationTokenSource = netMainCts;
|
||||
netMainTask = Task.Factory.StartNew(
|
||||
() => Updater(netMainCts.Token),
|
||||
netMainCts.Token,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default);
|
||||
|
||||
Thread threadReader = new(new ParameterizedThreadStart(PacketReader))
|
||||
{
|
||||
Name = "ProtocolPacketReader"
|
||||
};
|
||||
netReader = new Tuple<Thread, CancellationTokenSource>(threadReader, new CancellationTokenSource());
|
||||
threadReader.Start(netReader.Item2.Token);
|
||||
CancellationTokenSource netReaderCts = new();
|
||||
netReaderCancellationTokenSource = netReaderCts;
|
||||
netReaderTask = PacketReaderAsync(netReaderCts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -3865,7 +3890,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
/// <returns>Net read thread ID</returns>
|
||||
public int GetNetMainThreadId()
|
||||
{
|
||||
return netMain is not null ? netMain.Item1.ManagedThreadId : -1;
|
||||
return netMainThreadId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -3875,14 +3900,14 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
{
|
||||
try
|
||||
{
|
||||
if (netMain is not null)
|
||||
if (netMainCancellationTokenSource is not null)
|
||||
{
|
||||
netMain.Item2.Cancel();
|
||||
netMainCancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
if (netReader is not null)
|
||||
if (netReaderCancellationTokenSource is not null)
|
||||
{
|
||||
netReader.Item2.Cancel();
|
||||
netReaderCancellationTokenSource.Cancel();
|
||||
socketWrapper.Disconnect();
|
||||
}
|
||||
}
|
||||
|
|
@ -4106,7 +4131,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return true; //No need to check session or start encryption
|
||||
}
|
||||
default:
|
||||
HandlePacket(packetId, packetData);
|
||||
using (MainThreadExecutionScope.Enter(handler))
|
||||
HandlePacket(packetId, packetData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -4133,8 +4159,7 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
&& serverIDhash == session.ServerIDhash &&
|
||||
serverPublicKey.SequenceEqual(session.ServerPublicKey))
|
||||
{
|
||||
session.SessionPreCheckTask.Wait();
|
||||
if (session.SessionPreCheckTask.Result) // PreCheck Success
|
||||
if (session.SessionPreCheckTask.IsCompletedSuccessfully && session.SessionPreCheckTask.Result)
|
||||
needCheckSession = false;
|
||||
}
|
||||
|
||||
|
|
@ -4256,7 +4281,8 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return true;
|
||||
}
|
||||
default:
|
||||
HandlePacket(packetId, packetData);
|
||||
using (MainThreadExecutionScope.Enter(handler))
|
||||
HandlePacket(packetId, packetData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MinecraftClient.Crypto;
|
||||
|
||||
namespace MinecraftClient.Protocol.Handlers
|
||||
|
|
@ -68,6 +71,22 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveAsync(Memory<byte> buffer, CancellationToken cancellationToken)
|
||||
{
|
||||
int read = 0;
|
||||
while (read < buffer.Length)
|
||||
{
|
||||
int currentRead = encrypted
|
||||
? await s!.ReadAsync(buffer[read..], cancellationToken)
|
||||
: await c.GetStream().ReadAsync(buffer[read..], cancellationToken);
|
||||
|
||||
if (currentRead == 0)
|
||||
throw new IOException("Connection closed.");
|
||||
|
||||
read += currentRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read some data from the server.
|
||||
/// </summary>
|
||||
|
|
@ -84,6 +103,18 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
public async Task<byte[]> ReadDataRAWAsync(int length, CancellationToken cancellationToken)
|
||||
{
|
||||
if (length > 0)
|
||||
{
|
||||
byte[] cache = new byte[length];
|
||||
await ReceiveAsync(cache, cancellationToken);
|
||||
return cache;
|
||||
}
|
||||
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send raw data to the server.
|
||||
/// </summary>
|
||||
|
|
@ -99,6 +130,17 @@ namespace MinecraftClient.Protocol.Handlers
|
|||
c.Client.Send(buffer);
|
||||
}
|
||||
|
||||
public async Task SendDataRAWAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!IsConnected())
|
||||
throw new SocketException((int)SocketError.NotConnected);
|
||||
|
||||
if (encrypted)
|
||||
await s!.WriteAsync(buffer, cancellationToken);
|
||||
else
|
||||
await c.GetStream().WriteAsync(buffer, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the server
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue