Created
December 9, 2019 18:32
-
-
Save HurricanKai/1a07eeb198909865dfcbe0ddb33d5645 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Buffers; | |
using System.Buffers.Binary; | |
using System.Text; | |
using System.Text.Json; | |
using System.Threading.Tasks; | |
using Microsoft.AspNetCore.Connections; | |
using Microsoft.Extensions.Logging; | |
namespace Frontend | |
{ | |
public sealed class MCConnectionHandler : ConnectionHandler | |
{ | |
private readonly ILogger<MCConnectionHandler> _logger; | |
private readonly JsonSerializerOptions _jsonSerializerOptions; | |
public MCConnectionHandler(ILogger<MCConnectionHandler> logger) | |
{ | |
_logger = logger; | |
_jsonSerializerOptions = new JsonSerializerOptions() | |
{ | |
IgnoreNullValues = true | |
}; | |
} | |
public override async Task OnConnectedAsync(ConnectionContext connection) | |
{ | |
connection.Items["state"] = ConnectionState.Handshaking; | |
connection.Items["flush"] = false; | |
connection.Items["close"] = false; | |
connection.Items["statusPayload"] = new StatusResponsePayload | |
{ | |
Version = new StatusResponsePayload.VersionPayload() | |
{ | |
Name = "1.14.4", | |
Protocol = 498 | |
}, | |
Players = new StatusResponsePayload.PlayersPayload | |
{ | |
Max = 1000000, | |
Online = 999999, | |
Samples = new StatusResponsePayload.PlayersPayload.PlayerSample[0] | |
}, | |
Description = new Chat("Powered by SM3") | |
{ | |
Color = "red", | |
Obfuscated = true | |
}, | |
Favicon = null | |
}; | |
var memPool = MemoryPool<byte>.Shared; | |
await using var inputStream = connection.Transport.Input.AsStream(); | |
await using var outputStream = connection.Transport.Output.AsStream(); | |
while (!connection.ConnectionClosed.IsCancellationRequested) | |
{ | |
var length = StreamNetworkUtils.ReadVarInt(inputStream); | |
if (length <= 0) | |
{ | |
_logger.LogCritical($"Could not read Packet Length. Closing Connection without notice. E: {length}"); | |
connection.Abort(new ConnectionAbortedException("Could not read Packet Length")); | |
return; | |
} | |
using var inputPacketMemoryOwner = memPool.Rent(length); | |
var inputPacketMemory = inputPacketMemoryOwner.Memory; | |
var i = await inputStream.ReadAsync(inputPacketMemory, connection.ConnectionClosed); | |
if (i != length) | |
{ | |
_logger.LogCritical($"Read Length was not equal to Packet Length. Closing Connection without notice"); | |
connection.Abort(new ConnectionAbortedException("Read Length was not equal to Packet Length")); | |
return; | |
} | |
ParsePacket(inputPacketMemory, connection); | |
if ((bool) connection.Items["flush"]) | |
{ | |
await connection.Transport.Output.FlushAsync(); | |
connection.Items["flush"] = false; | |
} | |
if ((bool) connection.Items["close"]) | |
{ | |
connection.Items["close"] = false; | |
return; | |
} | |
} | |
} | |
private void ParsePacket(in Memory<byte> inputPacketMemory, ConnectionContext ctx) | |
{ | |
var span = inputPacketMemory.Span; | |
var id = SpanNetworkUtils.GetVarInt(ref span); | |
switch ((ConnectionState)ctx.Items["state"]) | |
{ | |
case ConnectionState.Handshaking: | |
switch (id) | |
{ | |
case 0x00: | |
var protocolVersion = SpanNetworkUtils.GetVarInt(ref span); | |
var serverAddress = SpanNetworkUtils.GetString(ref span); | |
var port = BinaryPrimitives.ReadInt16BigEndian(span); | |
span = span.Slice(sizeof(Int16)); | |
var nextState = (ConnectionState) SpanNetworkUtils.GetVarInt(ref span); | |
_logger.LogInformation($"Handshake successful"); | |
_logger.LogInformation($"Protocol: {protocolVersion}"); | |
_logger.LogInformation($"Connection from: {serverAddress}:{port}"); | |
_logger.LogInformation($"Remote Endpoint: {ctx.RemoteEndPoint}"); | |
_logger.LogInformation($"Switching to {Enum.GetName(typeof(ConnectionState), nextState)}"); | |
ctx.Items["state"] = nextState; | |
break; | |
default: | |
_logger.LogWarning($"Could not Handle Handshaking Packet {id:x2}"); | |
break; | |
} | |
break; | |
case ConnectionState.Login: | |
switch (id) | |
{ | |
case 0x00: | |
var username = SpanNetworkUtils.GetString(ref span); | |
_logger.LogInformation($"Username: {username}"); | |
_logger.LogInformation($"Attempting to Gracefully refuse Login"); | |
var disconnectMessage = JsonSerializer.SerializeToUtf8Bytes(new Chat("Sorry. We do not support Log in currently") | |
{ | |
Bold = true, | |
Color = "red" | |
}, _jsonSerializerOptions); | |
var dataSize = SpanNetworkUtils.GetVarIntSize(0x00) | |
+ SpanNetworkUtils.GetVarIntSize(disconnectMessage.Length) | |
+ disconnectMessage.Length; | |
var packetSpan = ctx.Transport.Output.GetSpan(dataSize + SpanNetworkUtils.GetVarIntSize(dataSize)); | |
var i = SpanNetworkUtils.WriteVarInt(packetSpan, dataSize); | |
i += SpanNetworkUtils.WriteVarInt(packetSpan.Slice(i), 0x00); | |
i += SpanNetworkUtils.WriteVarInt(packetSpan.Slice(i), disconnectMessage.Length); | |
disconnectMessage.CopyTo(packetSpan.Slice(i)); | |
i += disconnectMessage.Length; | |
ctx.Transport.Output.Advance(i); | |
ctx.Items["flush"] = true; | |
ctx.Items["close"] = true; | |
break; | |
default: | |
_logger.LogWarning($"Could not Handle Login Packet {id:x2}"); | |
break; | |
} | |
break; | |
case ConnectionState.Status: | |
switch (id) | |
{ | |
case 0x00: | |
_logger.LogInformation("Answering Status Request"); | |
var responsePayload = (StatusResponsePayload)ctx.Items["statusPayload"]; | |
var payloadData = JsonSerializer.SerializeToUtf8Bytes(responsePayload, _jsonSerializerOptions); | |
var msg = Encoding.UTF8.GetString(payloadData); | |
var payloadSize = SpanNetworkUtils.GetVarIntSize(0x00) | |
+ SpanNetworkUtils.GetVarIntSize(payloadData.Length) | |
+ payloadData.Length; | |
var packetSpan1 = ctx.Transport.Output.GetSpan(payloadSize + SpanNetworkUtils.GetVarIntSize(payloadSize)); | |
var i = SpanNetworkUtils.WriteVarInt(packetSpan1, payloadSize); | |
i += SpanNetworkUtils.WriteVarInt(packetSpan1.Slice(i), 0x00); | |
i += SpanNetworkUtils.WriteVarInt(packetSpan1.Slice(i), payloadData.Length); | |
payloadData.CopyTo(packetSpan1.Slice(i)); | |
i += payloadData.Length; | |
ctx.Transport.Output.Advance(i); | |
ctx.Items["flush"] = true; | |
break; | |
case 0x01: | |
_logger.LogInformation("Answering Ping"); | |
var seed = BinaryPrimitives.ReadInt64BigEndian(span); | |
var dataSize = sizeof(long) + SpanNetworkUtils.GetVarIntSize(0x01); | |
var packetSize = SpanNetworkUtils.GetVarIntSize(dataSize) + dataSize; | |
var packetSpan2 = ctx.Transport.Output.GetSpan(packetSize); | |
var j = SpanNetworkUtils.WriteVarInt(packetSpan2, dataSize); | |
j += SpanNetworkUtils.WriteVarInt(packetSpan2.Slice(j), 0x01); | |
BinaryPrimitives.WriteInt64BigEndian(packetSpan2.Slice(j), seed); | |
ctx.Transport.Output.Advance(j + sizeof(long)); | |
ctx.Items["flush"] = true; | |
break; | |
default: | |
_logger.LogWarning($"Could not Handle Status Packet {id:x2}"); | |
break; | |
} | |
break; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment