Skip to content

Commit

Permalink
Merge pull request #12 from Jump-King-Multiplayer/development
Browse files Browse the repository at this point in the history
Merge development into main
  • Loading branch information
Skippeh authored Jan 16, 2022
2 parents 51c5fed + 50665e4 commit d47c5a1
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 20 deletions.
5 changes: 5 additions & 0 deletions JKMP.Plugin.Multiplayer/Matchmaking/MatchmakingManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ await Client.Connect(endpoint,
{
break;
}
catch (Exception ex)
{
Logger.Error(ex, "Matchmaking thread raised an unhandled exception");
// Ignore for now to ensure we don't crash the game
}

if (matchmakingCancellationSource.IsCancellationRequested)
break;
Expand Down
133 changes: 117 additions & 16 deletions Matchmaking.Client/Networking/Framed.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
Expand Down Expand Up @@ -30,34 +31,134 @@ public Framed(TStream stream, TCodec sink)
if (!Stream.CanRead)
return default;

int numBytes;
Tuple<bool, ulong> msgLengthTuple = await ReadMessageLength(Stream);

if (!msgLengthTuple.Item1)
return default;

ulong messageLength = msgLengthTuple.Item2;

byte varIntLength = EncodingUtility.GetVarIntLength(messageLength);

if (messageLength > int.MaxValue || (int)messageLength > recvBuffer.Length)
throw new InvalidDataException($"Message too large (length = {msgLengthTuple})");

if (!await ReadBytes(Stream, (int)messageLength, recvBuffer))
return default;

// tfw no span
byte[] bytes = new byte[messageLength + varIntLength];

// Write message length to first n bytes
using (var writer = new BinaryWriter(new MemoryStream(bytes, true)))
{
writer.WriteVarInt(messageLength);
}

Array.Copy(recvBuffer, 0, bytes, varIntLength, (int)messageLength);

using var memoryStream = new MemoryStream(bytes);
using var reader = new BinaryReader(memoryStream);

try
{
numBytes = await Stream.ReadAsync(recvBuffer, 0, recvBuffer.Length, cancellationToken);
return Codec.Decode(reader);
}
catch (ObjectDisposedException) // Socket was closed
catch (FormatException ex)
{
return default;
throw new FormatException($"Decoding incoming message failed.\nBytes: [{BytesToString(bytes)}]", ex);
}
catch (TaskCanceledException)
}

private async Task<Tuple<bool, ulong>> ReadMessageLength(TStream stream)
{
Tuple<bool, ulong> Success(ulong val) => new(true, val);
Tuple<bool, ulong> Fail() => new(false, default);

byte[] discriminatorBytes = new byte[1];
if (!await ReadBytes(stream, 1, discriminatorBytes))
return Fail();

var varIntLength = EncodingUtility.GetVarIntLength(discriminatorBytes[0]);

switch (varIntLength)
{
return default;
case 1: // byte
return Success(discriminatorBytes[0]);
case 3: // ushort
{
byte[] bytes = new byte[2];

if (!await ReadBytes(stream, 2, bytes))
return Fail();

return Success(BitConverter.ToUInt16(bytes, 0));
}
case 5: // uint
{
byte[] bytes = new byte[4];

if (!await ReadBytes(stream, 4, bytes))
return Fail();

return Success(BitConverter.ToUInt32(bytes, 0));
}
case 9: // ulong
{
byte[] bytes = new byte[8];

if (!await ReadBytes(stream, 8, bytes))
return Fail();

return Success(BitConverter.ToUInt64(bytes, 0));
}
default: throw new NotImplementedException();
}
}

if (numBytes == 0) // EOF/Disconnected
return default;
private async Task<bool> ReadBytes(TStream stream, int length, byte[] buffer)
{
int numBytes = 0;

// tfw no span
byte[] bytes = new byte[numBytes];
Array.Copy(recvBuffer, bytes, numBytes);
while (numBytes < length)
{
int read;

using var memoryStream = new MemoryStream(bytes);
using var reader = new BinaryReader(memoryStream);
try
{
read = await stream.ReadAsync(buffer, numBytes, length - numBytes);
}
catch (ObjectDisposedException) // Socket was closed
{
return false;
}
catch (TaskCanceledException)
{
return false;
}

// todo: support reading multiple messages in a packet

return Codec.Decode(reader);
if (read == 0)
return false;

numBytes += read;
}

return true;
}

private string BytesToString(byte[] bytes)
{
StringBuilder builder = new();

for (int i = 0; i < bytes.Length; ++i)
{
builder.Append("0x" + bytes[i].ToString("X2"));

if (i < bytes.Length - 1)
builder.Append(", ");
}

return builder.ToString();
}

public async Task<bool> Send(TData data)
Expand Down
4 changes: 2 additions & 2 deletions Matchmaking.Client/Networking/MessagesCodec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ public override Message Decode(BinaryReader reader)
Message message = (Message)Activator.CreateInstance(clrType);
message.Deserialize(reader);

available = (ulong)(reader.BaseStream.Position - reader.BaseStream.Length);
available = (ulong)(reader.BaseStream.Length - reader.BaseStream.Position);

if (available > 0)
throw new FormatException($"Deserialized message did not consume the full length of the message (remaining bytes: {available}");
throw new FormatException($"Deserialized message did not consume the full length of the message (remaining bytes: {available})");

return message;
}
Expand Down
4 changes: 2 additions & 2 deletions Resources/UI/Chat/ChatMessage.xmmp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<Proportion Type="Auto" />
</HorizontalStackPanel.Proportions>
<HorizontalStackPanel>
<Label Id="Name" Font="Fonts/OpenSans-Bold.ttf:22" SupportsCommands="False" />
<Label Id="Time" Font="Fonts/OpenSans-Bold.ttf:16" VerticalAlignment="Center" Padding="5, 0, 0, 0" />
<Label Id="Name" Font="Fonts/OpenSans-Bold.ttf:22" SupportsCommands="False" Padding="0, 0, 5, 0" />
<Label Id="Time" Font="Fonts/OpenSans-Bold.ttf:16" VerticalAlignment="Center" />
</HorizontalStackPanel>
<Label Id="Channel" Font="Fonts/OpenSans-Bold.ttf:22" />
</HorizontalStackPanel>
Expand Down

0 comments on commit d47c5a1

Please sign in to comment.