Skip to content

Commit bd6deda

Browse files
authored
Merge pull request #77 from Klotzi111/main
Various improvements 2
2 parents baffc9d + 8e188b1 commit bd6deda

File tree

73 files changed

+3785
-390
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+3785
-390
lines changed

Components/MineSharp.Protocol/MinecraftClient.cs

Lines changed: 90 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -367,18 +367,18 @@ protected override void Unregister()
367367
/// <summary>
368368
/// Waits until a packet of the specified type is received and matches the given condition.
369369
/// </summary>
370-
/// <typeparam name="T">The type of the packet.</typeparam>
370+
/// <typeparam name="TPacket">The type of the packet.</typeparam>
371371
/// <param name="condition">A function that evaluates the packet and returns true if the condition is met.</param>
372372
/// <param name="cancellationToken">A token to cancel the wait for the matching packet.</param>
373373
/// <returns>A task that completes once a packet matching the condition is received.</returns>
374-
public Task WaitForPacketWhere<T>(Func<T, Task<bool>> condition, CancellationToken cancellationToken = default)
375-
where T : IPacket
374+
public Task<TPacket> WaitForPacketWhere<TPacket>(Func<TPacket, Task<bool>> condition, CancellationToken cancellationToken = default)
375+
where TPacket : IPacket
376376
{
377377
// linked token is required to cancel the task when the client is disconnected
378378
var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, CancellationToken);
379379
var token = cts.Token;
380-
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
381-
async Task PacketHandler(T packet)
380+
var tcs = new TaskCompletionSource<TPacket>(TaskCreationOptions.RunContinuationsAsynchronously);
381+
async Task PacketHandler(TPacket packet)
382382
{
383383
try
384384
{
@@ -388,7 +388,7 @@ async Task PacketHandler(T packet)
388388
}
389389
if (await condition(packet).WaitAsync(token))
390390
{
391-
tcs.TrySetResult();
391+
tcs.TrySetResult(packet);
392392
}
393393
}
394394
catch (OperationCanceledException e)
@@ -400,31 +400,33 @@ async Task PacketHandler(T packet)
400400
tcs.TrySetException(e);
401401
}
402402
}
403-
var packetRegistration = On<T>(PacketHandler);
403+
var packetRegistration = On<TPacket>(PacketHandler);
404404
if (packetRegistration == null)
405405
{
406406
// TODO: Can this occur?
407407
cts.Dispose();
408408
throw new InvalidOperationException("Could not register packet handler");
409409
}
410-
return tcs.Task.ContinueWith(_ =>
410+
// this registration is required because otherwise the task will only get cancelled when the next packet of that ype is received
411+
var cancellationRegistration = token.Register(() =>
412+
{
413+
// cancelling the tcs will later dispose the other stuff
414+
tcs.TrySetCanceled(token);
415+
});
416+
tcs.Task.ContinueWith(_ =>
411417
{
418+
cancellationRegistration.Dispose();
412419
packetRegistration.Dispose();
413420
cts.Dispose();
414421
}, TaskContinuationOptions.ExecuteSynchronously);
422+
return tcs.Task;
415423
}
416424

417-
/// <summary>
418-
/// Waits until a packet of the specified type is received and matches the given condition.
419-
/// </summary>
420-
/// <typeparam name="T">The type of the packet.</typeparam>
421-
/// <param name="condition">A function that evaluates the packet and returns true if the condition is met.</param>
422-
/// <param name="cancellationToken">A token to cancel the wait for the matching packet.</param>
423-
/// <returns>A task that completes once a packet matching the condition is received.</returns>
424-
public Task WaitForPacketWhere<T>(Func<T, bool> condition, CancellationToken cancellationToken = default)
425-
where T : IPacket
425+
/// <inheritdoc cref="WaitForPacketWhere{TPacket}(Func{TPacket, Task{bool}}, CancellationToken)"/>
426+
public Task<TPacket> WaitForPacketWhere<TPacket>(Func<TPacket, bool> condition, CancellationToken cancellationToken = default)
427+
where TPacket : IPacket
426428
{
427-
return WaitForPacketWhere<T>(packet => Task.FromResult(condition(packet)), cancellationToken);
429+
return WaitForPacketWhere<TPacket>(packet => Task.FromResult(condition(packet)), cancellationToken);
428430
}
429431

430432
/// <summary>
@@ -550,7 +552,7 @@ internal void HandleBundleDelimiter()
550552

551553
private async Task ProcessBundledPackets(ConcurrentQueue<(PacketType, PacketBuffer)> packets)
552554
{
553-
Logger.Debug($"Processing {packets.Count} bundled packets");
555+
Logger.Trace($"Processing {packets.Count} bundled packets");
554556
try
555557
{
556558
// wiki.vg: the client is guaranteed to process every packet in the bundle on the same tick
@@ -581,8 +583,9 @@ private async Task StreamLoop()
581583
try
582584
{
583585
// run both tasks in parallel
584-
var receiveTask = Task.Factory.StartNew(ReceivePackets, TaskCreationOptions.LongRunning);
585-
var sendTask = Task.Factory.StartNew(SendPackets, TaskCreationOptions.LongRunning);
586+
// because the task factory does not unwrap the tasks (like Task.Run) we need to do it manually
587+
var receiveTask = Task.Factory.StartNew(ReceivePackets, TaskCreationOptions.LongRunning).Unwrap();
588+
var sendTask = Task.Factory.StartNew(SendPackets, TaskCreationOptions.LongRunning).Unwrap();
586589

587590
// extract the exception from the task that finished first
588591
await await Task.WhenAny(receiveTask, sendTask);
@@ -601,79 +604,97 @@ private async Task StreamLoop()
601604

602605
private async Task ReceivePackets()
603606
{
604-
while (true)
607+
try
605608
{
606-
CancellationToken.ThrowIfCancellationRequested();
609+
while (true)
610+
{
611+
CancellationToken.ThrowIfCancellationRequested();
607612

608-
var buffer = stream!.ReadPacket();
613+
var buffer = stream!.ReadPacket();
609614

610-
var packetId = buffer.ReadVarInt();
611-
var gameState = gameStatePacketHandler.GameState;
612-
var packetType = Data.Protocol.GetPacketType(PacketFlow.Clientbound, gameState, packetId);
615+
var packetId = buffer.ReadVarInt();
616+
var gameState = gameStatePacketHandler.GameState;
617+
var packetType = Data.Protocol.GetPacketType(PacketFlow.Clientbound, gameState, packetId);
613618

614-
Logger.Trace("Received packet {PacketType}. GameState = {GameState}, PacketId = {PacketId}", packetType, gameState, packetId);
619+
Logger.Trace("Received packet {PacketType}. GameState = {GameState}, PacketId = {PacketId}", packetType, gameState, packetId);
615620

616-
// handle BundleDelimiter packet here, because there is a race condition where some
617-
// packets may be read before HandleBundleDelimiter is invoked through a handler
618-
if (packetType == PacketType.CB_Play_BundleDelimiter)
619-
{
620-
HandleBundleDelimiter();
621-
continue;
622-
}
621+
// handle BundleDelimiter packet here, because there is a race condition where some
622+
// packets may be read before HandleBundleDelimiter is invoked through a handler
623+
if (packetType == PacketType.CB_Play_BundleDelimiter)
624+
{
625+
HandleBundleDelimiter();
626+
continue;
627+
}
623628

624-
if (gameState != GameState.Play)
625-
{
626-
await HandleIncomingPacket(packetType, buffer);
627-
}
628-
else
629-
{
630-
var bundledPackets = this.bundledPackets;
631-
if (bundledPackets != null)
629+
if (gameState != GameState.Play)
632630
{
633-
bundledPackets.Enqueue((packetType, buffer));
631+
await HandleIncomingPacket(packetType, buffer);
634632
}
635633
else
636634
{
637-
// handle the packet in a new task to prevent blocking the stream loop
638-
_ = Task.Run(() => HandleIncomingPacket(packetType, buffer));
635+
var bundledPackets = this.bundledPackets;
636+
if (bundledPackets != null)
637+
{
638+
bundledPackets.Enqueue((packetType, buffer));
639+
}
640+
else
641+
{
642+
// handle the packet in a new task to prevent blocking the stream loop
643+
_ = Task.Run(() => HandleIncomingPacket(packetType, buffer));
644+
}
639645
}
640646
}
641647
}
648+
catch (Exception e)
649+
{
650+
Logger.Debug(e, "ReceivePackets loop ended with exception.");
651+
throw;
652+
}
653+
// can never exit without exception because infinite loop without break
642654
}
643655

644656
private async Task SendPackets()
645657
{
646-
await foreach (var task in packetQueue.ReceiveAllAsync())
658+
try
647659
{
648-
if (task.Token.IsCancellationRequested)
660+
await foreach (var task in packetQueue.ReceiveAllAsync())
649661
{
650-
task.Task.TrySetCanceled();
651-
continue;
652-
}
662+
if (task.Token.IsCancellationRequested)
663+
{
664+
task.Task.TrySetCanceled();
665+
continue;
666+
}
653667

654-
try
655-
{
656-
DispatchPacket(task.Packet);
657-
task.Task.TrySetResult();
658-
}
659-
catch (OperationCanceledException e)
660-
{
661-
task.Task.TrySetCanceled(e.CancellationToken);
662-
// we should stop. So we do by rethrowing the exception
663-
throw;
664-
}
665-
catch (Exception e)
666-
{
667-
Logger.Error(e, "Encountered exception while dispatching packet {PacketType}", task.Packet.Type);
668-
task.Task.TrySetException(e);
669-
if (e is SocketException)
668+
try
669+
{
670+
DispatchPacket(task.Packet);
671+
task.Task.TrySetResult();
672+
}
673+
catch (OperationCanceledException e)
670674
{
671-
// break the loop to prevent further packets from being sent
672-
// because the connection is probably dead
675+
task.Task.TrySetCanceled(e.CancellationToken);
676+
// we should stop. So we do by rethrowing the exception
673677
throw;
674678
}
679+
catch (Exception e)
680+
{
681+
Logger.Error(e, "Encountered exception while dispatching packet {PacketType}", task.Packet.Type);
682+
task.Task.TrySetException(e);
683+
if (e is SocketException)
684+
{
685+
// break the loop to prevent further packets from being sent
686+
// because the connection is probably dead
687+
throw;
688+
}
689+
}
675690
}
676691
}
692+
catch (Exception e)
693+
{
694+
Logger.Debug(e, "SendPackets loop ended with exception.");
695+
throw;
696+
}
697+
// can never exit without exception because infinite loop without break (because we never complete the BufferBlock we only cancel it)
677698
}
678699

679700
private void DispatchPacket(IPacket packet)

Components/MineSharp.Protocol/Packets/Clientbound/Configuration/PluginMessagePacket.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ namespace MineSharp.Protocol.Packets.Clientbound.Configuration;
88
/// <summary>
99
/// Plugin message packet
1010
/// </summary>
11-
/// <param name="ChannelName">The name of the channel the data was sent</param>
11+
/// <param name="Channel">The name of the channel the data was sent</param>
1212
/// <param name="Data">The message data</param>
13-
public sealed record PluginMessagePacket(Identifier ChannelName, byte[] Data) : IPacket
13+
public sealed record PluginMessagePacket(Identifier Channel, byte[] Data) : IPacket
1414
{
1515
/// <inheritdoc />
1616
public PacketType Type => StaticType;
@@ -20,16 +20,16 @@ public sealed record PluginMessagePacket(Identifier ChannelName, byte[] Data) :
2020
/// <inheritdoc />
2121
public void Write(PacketBuffer buffer, MinecraftData version)
2222
{
23-
buffer.WriteIdentifier(ChannelName);
23+
buffer.WriteIdentifier(Channel);
2424
buffer.WriteBytes(Data);
2525
}
2626

2727
/// <inheritdoc />
2828
public static IPacket Read(PacketBuffer buffer, MinecraftData version)
2929
{
30-
var channelName = buffer.ReadIdentifier();
30+
var channel = buffer.ReadIdentifier();
3131
var data = buffer.RestBuffer();
32-
return new PluginMessagePacket(channelName, data);
32+
return new PluginMessagePacket(channel, data);
3333
}
3434
}
3535

Lines changed: 5 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
using MineSharp.Core.Common;
2-
using MineSharp.Core.Serialization;
1+
using MineSharp.Core.Serialization;
32
using MineSharp.Data;
43
using MineSharp.Data.Protocol;
5-
using static MineSharp.Protocol.Packets.Clientbound.Configuration.UpdateTagsPacket;
4+
using MineSharp.Protocol.Packets.NetworkTypes;
65

76
namespace MineSharp.Protocol.Packets.Clientbound.Configuration;
87

@@ -20,88 +19,14 @@ public sealed record UpdateTagsPacket(Registry[] Registries) : IPacket
2019
/// <inheritdoc />
2120
public void Write(PacketBuffer buffer, MinecraftData version)
2221
{
23-
WriteRegistries(buffer, Registries);
22+
buffer.WriteVarIntArray(Registries, (buffer, registry) => registry.Write(buffer));
2423
}
2524

2625
/// <inheritdoc />
2726
public static IPacket Read(PacketBuffer buffer, MinecraftData version)
2827
{
29-
var registries = ReadRegistries(buffer);
30-
return new UpdateTagsPacket(registries);
31-
}
32-
33-
private static void WriteRegistries(PacketBuffer buffer, Registry[] registries)
34-
{
35-
buffer.WriteVarInt(registries.Length);
36-
foreach (var registry in registries)
37-
{
38-
buffer.WriteIdentifier(registry.Identifier);
39-
WriteTags(buffer, registry.Tags);
40-
}
41-
}
42-
43-
private static Registry[] ReadRegistries(PacketBuffer buffer)
44-
{
45-
var registryCount = buffer.ReadVarInt();
46-
var registries = new Registry[registryCount];
47-
for (int i = 0; i < registryCount; i++)
48-
{
49-
var registry = buffer.ReadIdentifier();
50-
var tags = ReadTags(buffer);
51-
registries[i] = new Registry(registry, tags);
52-
}
53-
return registries;
54-
}
55-
56-
private static void WriteTags(PacketBuffer buffer, Tag[] tags)
57-
{
58-
buffer.WriteVarInt(tags.Length);
59-
foreach (var tag in tags)
60-
{
61-
buffer.WriteIdentifier(tag.Name);
62-
buffer.WriteVarInt(tag.Entries.Length);
63-
foreach (var entry in tag.Entries)
64-
{
65-
buffer.WriteVarInt(entry);
66-
}
67-
}
68-
}
69-
70-
private static Tag[] ReadTags(PacketBuffer buffer)
71-
{
72-
var tagCount = buffer.ReadVarInt();
73-
var tags = new Tag[tagCount];
74-
for (int j = 0; j < tagCount; j++)
75-
{
76-
var tagName = buffer.ReadIdentifier();
77-
var entries = ReadEntries(buffer);
78-
tags[j] = new Tag(tagName, entries);
79-
}
80-
return tags;
81-
}
28+
var registries = buffer.ReadVarIntArray(Registry.Read);
8229

83-
private static int[] ReadEntries(PacketBuffer buffer)
84-
{
85-
var entryCount = buffer.ReadVarInt();
86-
var entries = new int[entryCount];
87-
for (int k = 0; k < entryCount; k++)
88-
{
89-
entries[k] = buffer.ReadVarInt();
90-
}
91-
return entries;
30+
return new UpdateTagsPacket(registries);
9231
}
93-
94-
/// <summary>
95-
/// Represents a registry with its tags
96-
/// </summary>
97-
/// <param name="Identifier">The registry identifier</param>
98-
/// <param name="Tags">Array of tags</param>
99-
public sealed record Registry(Identifier Identifier, Tag[] Tags);
100-
101-
/// <summary>
102-
/// Represents a tag with its entries
103-
/// </summary>
104-
/// <param name="Name">The tag name</param>
105-
/// <param name="Entries">Array of entries</param>
106-
public sealed record Tag(Identifier Name, int[] Entries);
10732
}

0 commit comments

Comments
 (0)